28232 lines
1.4 MiB
28232 lines
1.4 MiB
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: BillyGalbreath <blake.galbreath@gmail.com>
|
|
Date: Sun, 12 Jun 2022 09:18:57 -0500
|
|
Subject: [PATCH] Purpur Server Changes
|
|
|
|
Original: PurpurMC
|
|
Copyright (C) 2023 PurpurMC
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index 3ee1160c796cc86db9bc9438055b307239e9a8f7..9c01005751c0088f560f96401cdfdebbbda4e7ec 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -7,12 +7,8 @@ plugins {
|
|
}
|
|
|
|
dependencies {
|
|
- implementation(project(":pufferfish-api")) // Pufferfish // Paper
|
|
- // Pufferfish start
|
|
- implementation("io.papermc.paper:paper-mojangapi:1.19.2-R0.1-SNAPSHOT") {
|
|
- exclude("io.papermc.paper", "paper-api")
|
|
- }
|
|
- // Pufferfish end
|
|
+ implementation(project(":purpur-api")) // Purpur
|
|
+ implementation("io.papermc.paper:paper-mojangapi:1.19.4-R0.1-SNAPSHOT") // Purpur
|
|
// Paper start
|
|
implementation("org.jline:jline-terminal-jansi:3.21.0")
|
|
implementation("net.minecrell:terminalconsoleappender:1.3.0")
|
|
@@ -42,6 +38,10 @@ dependencies {
|
|
}
|
|
// Paper end
|
|
|
|
+ implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur
|
|
+ implementation("org.mozilla:rhino-engine:1.7.14") // Purpur
|
|
+ implementation("dev.omega24:upnp4j:1.0") // Purpur
|
|
+
|
|
runtimeOnly("org.apache.maven:maven-resolver-provider:3.8.5")
|
|
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3")
|
|
runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3")
|
|
@@ -81,7 +81,7 @@ tasks.jar {
|
|
attributes(
|
|
"Main-Class" to "org.bukkit.craftbukkit.Main",
|
|
"Implementation-Title" to "CraftBukkit",
|
|
- "Implementation-Version" to "git-Pufferfish-$implementationVersion", // Pufferfish
|
|
+ "Implementation-Version" to "git-Purpur-$implementationVersion", // Pufferfish // Purpur
|
|
"Implementation-Vendor" to date, // Paper
|
|
"Specification-Title" to "Bukkit",
|
|
"Specification-Version" to project.version,
|
|
@@ -153,7 +153,7 @@ fun TaskContainer.registerRunTask(
|
|
name: String,
|
|
block: JavaExec.() -> Unit
|
|
): TaskProvider<JavaExec> = register<JavaExec>(name) {
|
|
- group = "paper"
|
|
+ group = "paperweight" // Purpur
|
|
mainClass.set("org.bukkit.craftbukkit.Main")
|
|
standardInput = System.`in`
|
|
workingDir = rootProject.layout.projectDirectory
|
|
diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
index 692c962193cf9fcc6801fc93f3220bdc673d527b..8cde30544e14f8fc2dac32966ae3c21f8cf3a551 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("Pufferfish", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish
|
|
+ Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish // Purpur
|
|
|
|
metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
|
|
String minecraftVersion = Bukkit.getVersion();
|
|
@@ -602,16 +602,8 @@ public class Metrics {
|
|
}));
|
|
|
|
metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
|
|
- metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline"));
|
|
- final String paperVersion;
|
|
- final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion();
|
|
- if (implVersion != null) {
|
|
- final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1);
|
|
- paperVersion = "git-Pufferfish-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); // Pufferfish
|
|
- } else {
|
|
- paperVersion = "unknown";
|
|
- }
|
|
- metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> paperVersion)); // Pufferfish
|
|
+ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur
|
|
+ metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // Purpur
|
|
|
|
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
|
|
Map<String, Map<String, Integer>> map = new HashMap<>();
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
|
|
index 9d687da5bdf398bb3f6c84cdf1249a7213d09f2e..462a6eed350fd660ddaf25d567bb6e97b77d0b2b 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
|
|
@@ -19,8 +19,10 @@ import java.util.stream.StreamSupport;
|
|
|
|
public class PaperVersionFetcher implements VersionFetcher {
|
|
private static final java.util.regex.Pattern VER_PATTERN = java.util.regex.Pattern.compile("^([0-9\\.]*)\\-.*R"); // R is an anchor, will always give '-R' at end
|
|
- private static final String GITHUB_BRANCH_NAME = "master";
|
|
- private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads/paper";
|
|
+ // Purpur start
|
|
+ private static final String DOWNLOAD_PAGE = "https://purpurmc.org/downloads";
|
|
+ private static int distance = -2; public int distance() { return distance; }
|
|
+ // Purpur end
|
|
private static @Nullable String mcVer;
|
|
|
|
@Override
|
|
@@ -31,11 +33,11 @@ public class PaperVersionFetcher implements VersionFetcher {
|
|
@Nonnull
|
|
@Override
|
|
public Component getVersionMessage(@Nonnull String serverVersion) {
|
|
- String[] parts = serverVersion.substring("git-Paper-".length()).split("[-\\s]");
|
|
- final Component updateMessage = getUpdateStatusMessage("PaperMC/Paper", GITHUB_BRANCH_NAME, parts[0]);
|
|
+ String[] parts = serverVersion.substring("git-Purpur-".length()).split("[-\\s]"); // Purpur
|
|
+ final Component updateMessage = getUpdateStatusMessage("PurpurMC/Purpur", "ver/" + getMinecraftVersion(), parts[0]); // Purpur
|
|
final Component history = getHistory();
|
|
|
|
- return history != null ? TextComponent.ofChildren(updateMessage, Component.newline(), history) : updateMessage;
|
|
+ return history != null ? Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()), history, updateMessage) : updateMessage; // Purpur
|
|
}
|
|
|
|
private static @Nullable String getMinecraftVersion() {
|
|
@@ -45,7 +47,7 @@ public class PaperVersionFetcher implements VersionFetcher {
|
|
String result = matcher.group();
|
|
mcVer = result.substring(0, result.length() - 2); // strip 'R' anchor and trailing '-'
|
|
} else {
|
|
- org.bukkit.Bukkit.getLogger().warning("Unable to match version to pattern! Report to PaperMC!");
|
|
+ org.bukkit.Bukkit.getLogger().warning("Unable to match version to pattern! Report to Purpur!"); // Purpur
|
|
org.bukkit.Bukkit.getLogger().warning("Pattern: " + VER_PATTERN.toString());
|
|
org.bukkit.Bukkit.getLogger().warning("Version: " + org.bukkit.Bukkit.getBukkitVersion());
|
|
}
|
|
@@ -55,7 +57,7 @@ public class PaperVersionFetcher implements VersionFetcher {
|
|
}
|
|
|
|
private static Component getUpdateStatusMessage(@Nonnull String repo, @Nonnull String branch, @Nonnull String versionInfo) {
|
|
- int distance;
|
|
+ //int distance; // Purpur - use field
|
|
try {
|
|
int jenkinsBuild = Integer.parseInt(versionInfo);
|
|
distance = fetchDistanceFromSiteApi(jenkinsBuild, getMinecraftVersion());
|
|
@@ -66,13 +68,13 @@ public class PaperVersionFetcher implements VersionFetcher {
|
|
|
|
switch (distance) {
|
|
case -1:
|
|
- return Component.text("Error obtaining version information", NamedTextColor.YELLOW);
|
|
+ return Component.text("* Error obtaining version information", NamedTextColor.RED); // Purpur
|
|
case 0:
|
|
- return Component.text("You are running the latest version", NamedTextColor.GREEN);
|
|
+ return Component.text("* You are running the latest version", NamedTextColor.GREEN); // Purpur
|
|
case -2:
|
|
- return Component.text("Unknown version", NamedTextColor.YELLOW);
|
|
+ return Component.text("* Unknown version", NamedTextColor.YELLOW); // Purpur
|
|
default:
|
|
- return Component.text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW)
|
|
+ return Component.text("* You are " + distance + " version(s) behind", NamedTextColor.YELLOW) // Purpur
|
|
.append(Component.newline())
|
|
.append(Component.text("Download the new version at: ")
|
|
.append(Component.text(DOWNLOAD_PAGE, NamedTextColor.GOLD)
|
|
@@ -85,15 +87,11 @@ public class PaperVersionFetcher implements VersionFetcher {
|
|
if (siteApiVersion == null) { return -1; }
|
|
try {
|
|
try (BufferedReader reader = Resources.asCharSource(
|
|
- new URL("https://api.papermc.io/v2/projects/paper/versions/" + siteApiVersion),
|
|
+ new URL("https://api.purpurmc.org/v2/purpur/" + siteApiVersion), // Purpur
|
|
Charsets.UTF_8
|
|
).openBufferedStream()) {
|
|
JsonObject json = new Gson().fromJson(reader, JsonObject.class);
|
|
- JsonArray builds = json.getAsJsonArray("builds");
|
|
- int latest = StreamSupport.stream(builds.spliterator(), false)
|
|
- .mapToInt(e -> e.getAsInt())
|
|
- .max()
|
|
- .getAsInt();
|
|
+ int latest = json.getAsJsonObject("builds").getAsJsonPrimitive("latest").getAsInt(); // Purpur
|
|
return latest - jenkinsBuild;
|
|
} catch (JsonSyntaxException ex) {
|
|
ex.printStackTrace();
|
|
@@ -144,6 +142,6 @@ public class PaperVersionFetcher implements VersionFetcher {
|
|
return null;
|
|
}
|
|
|
|
- return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC);
|
|
+ return org.bukkit.ChatColor.parseMM("<grey>Previous: %s", oldVersion); // Purpur
|
|
}
|
|
}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
|
|
index c5d5648f4ca603ef2b1df723b58f9caf4dd3c722..3cb56595822799926a8141e60a42f5d1edfc6de5 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
|
|
@@ -17,7 +17,7 @@ public final class PaperConsole extends SimpleTerminalConsole {
|
|
@Override
|
|
protected LineReader buildReader(LineReaderBuilder builder) {
|
|
builder
|
|
- .appName("Paper")
|
|
+ .appName("Purpur") // Purpur
|
|
.variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history"))
|
|
.completer(new ConsoleCommandCompleter(this.server))
|
|
.option(LineReader.Option.COMPLETE_IN_WORD, true);
|
|
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
index a08c00b8c0488d18be5e182f7892e5ab71d12247..338f693d098b6ab507c30f6411c9a952c34ba8e3 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
@@ -136,6 +136,10 @@ public class MobGoalHelper {
|
|
static {
|
|
// TODO these kinda should be checked on each release, in case obfuscation changes
|
|
deobfuscationMap.put("abstract_skeleton_1", "abstract_skeleton_melee");
|
|
+ // Purpur start
|
|
+ deobfuscationMap.put("zombie_1", "zombie_attack_villager");
|
|
+ deobfuscationMap.put("drowned_1", "drowned_attack_villager");
|
|
+ // Purpur end
|
|
|
|
ignored.add("goal_selector_1");
|
|
ignored.add("goal_selector_2");
|
|
diff --git a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java
|
|
index fa56cd09102a89692b42f1d14257990508c5c720..f9251183df72ddc56662fd3f02acf21641a2200c 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java
|
|
@@ -58,7 +58,7 @@ public class RAMDetails extends JList<String> {
|
|
GraphData data = RAMGraph.DATA.peekLast();
|
|
Vector<String> vector = new Vector<>();
|
|
|
|
- double[] tps = new double[] {server.tps1.getAverage(), server.tps5.getAverage(), server.tps15.getAverage()};
|
|
+ double[] tps = new double[] {server.tps5s.getAverage(), server.tps1.getAverage(), server.tps5.getAverage(), server.tps15.getAverage()}; // Purpur
|
|
String[] tpsAvg = new String[tps.length];
|
|
|
|
for ( int g = 0; g < tps.length; g++) {
|
|
@@ -67,7 +67,7 @@ public class RAMDetails extends JList<String> {
|
|
vector.add("Memory use: " + (data.getUsedMem() / 1024L / 1024L) + " mb (" + (data.getFree() * 100L / data.getMax()) + "% free)");
|
|
vector.add("Heap: " + (data.getTotal() / 1024L / 1024L) + " / " + (data.getMax() / 1024L / 1024L) + " mb");
|
|
vector.add("Avg tick: " + DECIMAL_FORMAT.format(getAverage(server.tickTimes)) + " ms");
|
|
- vector.add("TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg));
|
|
+ vector.add("TPS from last 5s, 1m, 5m, 15m: " + String.join(", ", tpsAvg)); // Purpur
|
|
|
|
setListData(vector);
|
|
}
|
|
diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
|
|
index 0dd3374468e05f7a312ba5856b9cf8a4787dfa59..b4e5fbace85c67e7bd347e6a90514bbc2c132d5e 100644
|
|
--- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
|
|
+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
|
|
@@ -28,6 +28,7 @@ public class PufferfishConfig {
|
|
|
|
private static final YamlFile config = new YamlFile();
|
|
private static int updates = 0;
|
|
+ public static File pufferfishFile; // Purpur
|
|
|
|
private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) {
|
|
ConfigurationSection newSection = new MemoryConfiguration();
|
|
@@ -50,7 +51,7 @@ public class PufferfishConfig {
|
|
}
|
|
|
|
public static void load() throws IOException {
|
|
- File configFile = new File("pufferfish.yml");
|
|
+ File configFile = pufferfishFile; // Purpur
|
|
|
|
if (configFile.exists()) {
|
|
try {
|
|
@@ -224,7 +225,7 @@ public class PufferfishConfig {
|
|
public static int activationDistanceMod;
|
|
|
|
private static void dynamicActivationOfBrains() throws IOException {
|
|
- dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true);
|
|
+ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", false); // Purpur
|
|
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.");
|
|
@@ -268,7 +269,7 @@ public class PufferfishConfig {
|
|
|
|
public static boolean throttleInactiveGoalSelectorTick;
|
|
private static void inactiveGoalSelectorThrottle() {
|
|
- throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true,
|
|
+ throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", false, // Purpur
|
|
"Throttles the AI goal selector in entity inactive ticks.",
|
|
"This can improve performance by a few percent, but has minor gameplay implications.");
|
|
}
|
|
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
index ad3560284ae79b9c6bbc8752be7d9d14b18e226e..37d7d89aac826e5e9c072cc82c64ca9192173e91 100644
|
|
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
@@ -922,9 +922,9 @@ public final class ChunkHolderManager {
|
|
}
|
|
|
|
public boolean processTicketUpdates() {
|
|
- co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager
|
|
+ //co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager // Purpur
|
|
return this.processTicketUpdates(true, true, null);
|
|
- } finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager
|
|
+ //} finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager // Purpur
|
|
}
|
|
|
|
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
|
|
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
|
|
index 8013dd333e27aa5fd0beb431fa32491eec9f5246..e42eb93fd9f6f51ff5bb4b14a2304d4ffcdd8441 100644
|
|
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
|
|
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
|
|
@@ -1750,7 +1750,7 @@ public final class NewChunkHolder {
|
|
boolean canSavePOI = !(chunk instanceof LevelChunk levelChunk && levelChunk.mustNotSave) && (poi != null && poi.isDirty());
|
|
boolean canSaveEntities = entities != null;
|
|
|
|
- try (co.aikar.timings.Timing ignored = this.world.timings.chunkSave.startTiming()) { // Paper
|
|
+ //try (co.aikar.timings.Timing ignored = this.world.timings.chunkSave.startTiming()) { // Paper // Purpur
|
|
if (canSaveChunk) {
|
|
canSaveChunk = this.saveChunk(chunk, unloading);
|
|
}
|
|
@@ -1764,7 +1764,7 @@ public final class NewChunkHolder {
|
|
this.lastEntityUnload = null;
|
|
}
|
|
}
|
|
- }
|
|
+ //} // Purpur
|
|
|
|
return executedUnloadTask | canSaveChunk | canSaveEntities | canSavePOI ? new SaveStat(executedUnloadTask || canSaveChunk, canSaveEntities, canSavePOI): null;
|
|
}
|
|
diff --git a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
|
|
index f0fce4113fb07c64adbec029d177c236cbdcbae8..e94224ed280247ee69dfdff8dc960f2b8729be33 100644
|
|
--- a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
|
|
+++ b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
|
|
@@ -78,10 +78,10 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
this.setAliases(Arrays.asList("pl"));
|
|
}
|
|
|
|
- private static <T> List<Component> formatProviders(TreeMap<String, PluginProvider<T>> plugins) {
|
|
+ private static <T> List<Component> formatProviders(TreeMap<String, PluginProvider<T>> plugins, @NotNull CommandSender sender) { // Purpur
|
|
List<Component> components = new ArrayList<>(plugins.size());
|
|
for (PluginProvider<T> entry : plugins.values()) {
|
|
- components.add(formatProvider(entry));
|
|
+ components.add(formatProvider(entry, sender)); // Purpur
|
|
}
|
|
|
|
boolean isFirst = true;
|
|
@@ -109,7 +109,7 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
return formattedSublists;
|
|
}
|
|
|
|
- private static Component formatProvider(PluginProvider<?> provider) {
|
|
+ private static Component formatProvider(PluginProvider<?> provider, @NotNull CommandSender sender) { // Purpur
|
|
TextComponent.Builder builder = Component.text();
|
|
if (provider instanceof SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) {
|
|
builder.append(LEGACY_PLUGIN_STAR);
|
|
@@ -117,12 +117,64 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
|
|
String name = provider.getMeta().getName();
|
|
Component pluginName = Component.text(name, fromStatus(provider))
|
|
- .clickEvent(ClickEvent.runCommand("/version " + name));
|
|
+ // Purpur start
|
|
+ .clickEvent(ClickEvent.suggestCommand("/version " + name));
|
|
+
|
|
+ if (sender instanceof org.bukkit.entity.Player && sender.hasPermission("bukkit.command.version")) {
|
|
+ // Event components
|
|
+ String description = provider.getMeta().getDescription();
|
|
+ TextComponent.Builder hover = Component.text();
|
|
+ hover.append(Component.text("Version: ", NamedTextColor.WHITE)).append(Component.text(provider.getMeta().getVersion(), NamedTextColor.GREEN));
|
|
+
|
|
+ if (description != null) {
|
|
+ hover.append(Component.newline())
|
|
+ .append(Component.text("Description: ", NamedTextColor.WHITE))
|
|
+ .append(Component.text(description, NamedTextColor.GREEN));
|
|
+ }
|
|
+
|
|
+ if (provider.getMeta().getWebsite() != null) {
|
|
+ hover.append(Component.newline())
|
|
+ .append(Component.text("Website: ", NamedTextColor.WHITE))
|
|
+ .append(Component.text(provider.getMeta().getWebsite(), NamedTextColor.GREEN));
|
|
+ }
|
|
+
|
|
+ if (!provider.getMeta().getAuthors().isEmpty()) {
|
|
+ hover.append(Component.newline());
|
|
+ if (provider.getMeta().getAuthors().size() == 1) {
|
|
+ hover.append(Component.text("Author: "));
|
|
+ } else {
|
|
+ hover.append(Component.text("Authors: "));
|
|
+ }
|
|
+
|
|
+ hover.append(getAuthors(provider.getMeta()));
|
|
+ }
|
|
+
|
|
+ pluginName.hoverEvent(hover.build());
|
|
+ }
|
|
|
|
builder.append(pluginName);
|
|
+ // Purpur end
|
|
+
|
|
+ return builder.build();
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @NotNull
|
|
+ private static TextComponent getAuthors(@NotNull final PluginMeta pluginMeta) {
|
|
+ TextComponent.Builder builder = Component.text();
|
|
+ List<String> authors = pluginMeta.getAuthors();
|
|
+
|
|
+ for (int i = 0; i < authors.size(); i++) {
|
|
+ if (i > 0) {
|
|
+ builder.append(Component.text(i < authors.size() - 1 ? ", " : " and ", NamedTextColor.WHITE));
|
|
+ }
|
|
+
|
|
+ builder.append(Component.text(authors.get(i), NamedTextColor.GREEN));
|
|
+ }
|
|
|
|
return builder.build();
|
|
}
|
|
+ // Purpur end
|
|
|
|
private static Component asPlainComponents(String strings) {
|
|
net.kyori.adventure.text.TextComponent.Builder builder = Component.text();
|
|
@@ -182,24 +234,24 @@ public class PaperPluginsCommand extends BukkitCommand {
|
|
}
|
|
}
|
|
|
|
- Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE);
|
|
+ //Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE);
|
|
//.append(INFO_ICON_START.hoverEvent(SERVER_PLUGIN_INFO)); TODO: Add docs
|
|
|
|
- sender.sendMessage(infoMessage);
|
|
+ //sender.sendMessage(infoMessage); // Purpur
|
|
|
|
if (!paperPlugins.isEmpty()) {
|
|
- sender.sendMessage(PAPER_HEADER);
|
|
+ sender.sendMessage(PAPER_HEADER.append(Component.text(" (%s):".formatted(paperPlugins.size())))); // Purpur
|
|
}
|
|
|
|
- for (Component component : formatProviders(paperPlugins)) {
|
|
+ for (Component component : formatProviders(paperPlugins, sender)) { // Purpur
|
|
sender.sendMessage(component);
|
|
}
|
|
|
|
if (!spigotPlugins.isEmpty()) {
|
|
- sender.sendMessage(BUKKIT_HEADER);
|
|
+ sender.sendMessage(BUKKIT_HEADER.append(Component.text(" (%s):".formatted(spigotPlugins.size())))); // Purpur
|
|
}
|
|
|
|
- for (Component component : formatProviders(spigotPlugins)) {
|
|
+ for (Component component : formatProviders(spigotPlugins, sender)) { // Purpur
|
|
sender.sendMessage(component);
|
|
}
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/console/HexFormattingConverter.java b/src/main/java/io/papermc/paper/console/HexFormattingConverter.java
|
|
index b9922b07cb105618390187d98acdf89e728e1f5a..6a1eda942aa33fc0802066416f8bc64f5f15d011 100644
|
|
--- a/src/main/java/io/papermc/paper/console/HexFormattingConverter.java
|
|
+++ b/src/main/java/io/papermc/paper/console/HexFormattingConverter.java
|
|
@@ -38,6 +38,7 @@ public final class HexFormattingConverter extends LogEventPatternConverter {
|
|
private static final String ANSI_RESET = "\u001B[m";
|
|
|
|
private static final char COLOR_CHAR = 0x7f;
|
|
+ private static final char LEGACY_CHAR = 0xa7; // Purpur
|
|
public static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.builder()
|
|
.hexColors()
|
|
.flattener(PaperAdventure.FLATTENER)
|
|
@@ -49,6 +50,8 @@ public final class HexFormattingConverter extends LogEventPatternConverter {
|
|
private static final String RESET_RGB_ANSI = ANSI_RESET + RGB_ANSI;
|
|
private static final Pattern NAMED_PATTERN = Pattern.compile(COLOR_CHAR + "[0-9a-fk-orA-FK-OR]");
|
|
private static final Pattern RGB_PATTERN = Pattern.compile(COLOR_CHAR + "#([0-9a-fA-F]){6}");
|
|
+ private static final Pattern LEGACY_RGB_PATTERN = Pattern.compile(LEGACY_CHAR + "x((" + LEGACY_CHAR + "[0-9a-fA-F]){6})"); // Purpur
|
|
+ private static final Pattern LEGACY_PATTERN = Pattern.compile(LEGACY_CHAR + "([0-9a-fk-orxA-FK-ORX])"); // Purpur
|
|
|
|
private static final String[] RGB_ANSI_CODES = new String[]{
|
|
formatHexAnsi(NamedTextColor.BLACK), // Black §0
|
|
@@ -134,7 +137,21 @@ public final class HexFormattingConverter extends LogEventPatternConverter {
|
|
}
|
|
|
|
private static String convertRGBColors(final String input) {
|
|
- return RGB_PATTERN.matcher(input).replaceAll(result -> {
|
|
+ // Purpur start - lets just shove this back in place
|
|
+ Matcher matcher = LEGACY_RGB_PATTERN.matcher(input);
|
|
+ StringBuilder buffer = new StringBuilder();
|
|
+ while (matcher.find()) {
|
|
+ String s = matcher.group().replace(String.valueOf(LEGACY_CHAR), "").replace('x', '#');
|
|
+ int hex = Integer.decode(s);
|
|
+ int red = (hex >> 16) & 0xFF;
|
|
+ int green = (hex >> 8) & 0xFF;
|
|
+ int blue = hex & 0xFF;
|
|
+ String replacement = String.format(RGB_ANSI, red, green, blue);
|
|
+ matcher.appendReplacement(buffer, replacement);
|
|
+ }
|
|
+ matcher.appendTail(buffer);
|
|
+ return RGB_PATTERN.matcher(buffer.toString()).replaceAll(result -> {
|
|
+ // Purpur end
|
|
final int hex = Integer.decode(result.group().substring(1));
|
|
return formatHexAnsi(hex);
|
|
});
|
|
@@ -152,10 +169,11 @@ public final class HexFormattingConverter extends LogEventPatternConverter {
|
|
}
|
|
|
|
private static String stripRGBColors(final String input) {
|
|
- return RGB_PATTERN.matcher(input).replaceAll("");
|
|
+ return LEGACY_RGB_PATTERN.matcher(RGB_PATTERN.matcher(input).replaceAll("")).replaceAll(""); // Purpur
|
|
}
|
|
|
|
static void format(String content, StringBuilder result, int start, boolean ansi) {
|
|
+ content = LEGACY_PATTERN.matcher(content).replaceAll(COLOR_CHAR + "$1"); // Purpur
|
|
int next = content.indexOf(COLOR_CHAR);
|
|
int last = content.length() - 1;
|
|
if (next == -1 || next == last) {
|
|
diff --git a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
|
|
index a8e813ca89b033f061e695288b3383bdcf128531..1ab65af9359d19530bba7f985a604d2a430ee234 100644
|
|
--- a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
|
|
+++ b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
|
|
@@ -54,9 +54,9 @@ public final class SysoutCatcher {
|
|
final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(clazz);
|
|
|
|
// Instead of just printing the message, send it to the plugin's logger
|
|
- plugin.getLogger().log(this.level, this.prefix + line);
|
|
+ plugin.getLogger().log(this.level, /*this.prefix +*/ line); // Purpur - prefix not needed
|
|
|
|
- if (SysoutCatcher.SUPPRESS_NAGS) {
|
|
+ if (true || SysoutCatcher.SUPPRESS_NAGS) { // Purpur - nagging is annoying
|
|
return;
|
|
}
|
|
if (SysoutCatcher.NAG_INTERVAL > 0 || SysoutCatcher.NAG_TIMEOUT > 0) {
|
|
diff --git a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
|
|
index 89bf48fd581ee6580b91e2eb31dd532cb622df5e..e35da199be67e04c34df6bc09afd8d8122cb0487 100644
|
|
--- a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
|
|
+++ b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
|
|
@@ -102,6 +102,7 @@ public class PluginInitializerManager {
|
|
java.util.List<File> files = (java.util.List<File>) optionSet.valuesOf("add-plugin");
|
|
// Register plugins from the flag
|
|
io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.PluginFlagProviderSource.INSTANCE, files);
|
|
+ io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.SparkProviderSource.INSTANCE, new File("cache", "spark.jar").toPath()); // Purpur
|
|
}
|
|
|
|
// This will be the end of me...
|
|
diff --git a/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java b/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..a7d1ae53eac94bc2dcf8bc78ef1da0d3b8554736
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java
|
|
@@ -0,0 +1,102 @@
|
|
+package io.papermc.paper.plugin.provider.source;
|
|
+
|
|
+import com.mojang.logging.LogUtils;
|
|
+import io.papermc.paper.plugin.entrypoint.Entrypoint;
|
|
+import io.papermc.paper.plugin.entrypoint.EntrypointHandler;
|
|
+import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
|
|
+import io.papermc.paper.plugin.provider.PluginProvider;
|
|
+import java.io.BufferedReader;
|
|
+import java.io.File;
|
|
+import java.io.InputStreamReader;
|
|
+import java.math.BigInteger;
|
|
+import java.net.URL;
|
|
+import java.net.URLConnection;
|
|
+import java.nio.file.Files;
|
|
+import java.nio.file.Path;
|
|
+import java.nio.file.StandardCopyOption;
|
|
+import java.security.MessageDigest;
|
|
+import java.util.stream.Collectors;
|
|
+import org.bukkit.plugin.java.JavaPlugin;
|
|
+import org.slf4j.Logger;
|
|
+
|
|
+public class SparkProviderSource extends FileProviderSource {
|
|
+ public static final SparkProviderSource INSTANCE = new SparkProviderSource();
|
|
+
|
|
+ private static final Logger LOGGER = LogUtils.getClassLogger();
|
|
+
|
|
+ public SparkProviderSource() {
|
|
+ super("File '%s' specified by Purpur"::formatted);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void registerProviders(EntrypointHandler entrypointHandler, Path context) throws Exception {
|
|
+ // first, check if user doesn't want spark at all
|
|
+ if (Boolean.getBoolean("Purpur.IReallyDontWantSpark")) {
|
|
+ return; // boo!
|
|
+ }
|
|
+
|
|
+ // second, check if user has their own spark
|
|
+ if (hasSpark()) {
|
|
+ LOGGER.info("Purpur: Using user-provided spark plugin instead of our own.");
|
|
+ return; // let's hope it's at least the modern version :3
|
|
+ }
|
|
+
|
|
+ // you can't have errors in your code if you wrap the entire codebase in a try/catch block
|
|
+ try {
|
|
+
|
|
+ // make sure the directory exists where we want to keep spark
|
|
+ File file = context.toFile();
|
|
+ file.getParentFile().mkdirs();
|
|
+
|
|
+ boolean shouldDownload;
|
|
+
|
|
+ // check if our spark exists
|
|
+ if (!file.exists()) {
|
|
+ // it does not, so let's download it
|
|
+ shouldDownload = true;
|
|
+ } else {
|
|
+ // we have a spark file, let's see if it's up-to-date by comparing shas
|
|
+ String fileSha1 = String.format("%040x", new BigInteger(1, MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(file.toPath()))));
|
|
+ String sparkSha1;
|
|
+
|
|
+ // luck has a nifty endpoint containing the sha of the newest version
|
|
+ URLConnection urlConnection = new URL("https://sparkapi.lucko.me/download/bukkit/sha1").openConnection();
|
|
+
|
|
+ // set a reasonable timeout to prevent servers without internet from hanging for 60+ seconds on startup
|
|
+ urlConnection.setReadTimeout(5000);
|
|
+ urlConnection.setConnectTimeout(5000);
|
|
+
|
|
+ // read it
|
|
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
|
|
+ sparkSha1 = reader.lines().collect(Collectors.joining(""));
|
|
+ }
|
|
+
|
|
+ // compare; we only download a new spark if the shas don't match
|
|
+ shouldDownload = !fileSha1.equals(sparkSha1);
|
|
+ }
|
|
+
|
|
+ // ok, finally we can download spark if we need it
|
|
+ if (shouldDownload) {
|
|
+ URLConnection urlConnection = new URL("https://sparkapi.lucko.me/download/bukkit").openConnection();
|
|
+ urlConnection.setReadTimeout(5000);
|
|
+ urlConnection.setConnectTimeout(5000);
|
|
+ Files.copy(urlConnection.getInputStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
|
+ }
|
|
+
|
|
+ // register the spark, newly downloaded or existing
|
|
+ super.registerProviders(entrypointHandler, context);
|
|
+
|
|
+ } catch (Throwable e) {
|
|
+ LOGGER.error("Purpur: Failed to download and install spark plugin", e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static boolean hasSpark() {
|
|
+ for (PluginProvider<JavaPlugin> provider : LaunchEntryPointHandler.INSTANCE.get(Entrypoint.PLUGIN).getRegisteredProviders()) {
|
|
+ if (provider.getMeta().getName().equalsIgnoreCase("spark")) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
|
|
index abe37c7c3c6f5ab73afd738ec78f06d7e4d2ed96..b5b6657e52e4f7a630229bd3ba433438af293e22 100644
|
|
--- a/src/main/java/net/minecraft/CrashReport.java
|
|
+++ b/src/main/java/net/minecraft/CrashReport.java
|
|
@@ -123,6 +123,10 @@ public class CrashReport {
|
|
StringBuilder stringbuilder = new StringBuilder();
|
|
|
|
stringbuilder.append("---- Minecraft Crash Report ----\n");
|
|
+ // Purpur start
|
|
+ stringbuilder.append("// ");
|
|
+ stringbuilder.append("// DO NOT REPORT THIS TO PAPER! REPORT TO PURPUR INSTEAD!");
|
|
+ // Purpur end
|
|
stringbuilder.append("// ");
|
|
stringbuilder.append(CrashReport.getErrorComment());
|
|
stringbuilder.append("\n\n");
|
|
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
|
index 7b6b51392b123d34382233adcf4c3d4867bdaa32..941f3a0d50329658a9380500ef039d7f10a284e2 100644
|
|
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
|
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
|
|
@@ -212,6 +212,21 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur start
|
|
+ public boolean testPermission(int i, String bukkitPermission) {
|
|
+ if (hasPermission(i, bukkitPermission)) {
|
|
+ return true;
|
|
+ }
|
|
+ String permissionMessage = getLevel().getServer().server.getPermissionMessage();
|
|
+ if (!permissionMessage.isBlank()) {
|
|
+ for (String line : permissionMessage.replace("<permission>", bukkitPermission).split("\n")) {
|
|
+ sendFailure(Component.literal(line));
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public Vec3 getPosition() {
|
|
return this.worldPosition;
|
|
}
|
|
@@ -317,6 +332,30 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void sendSuccess(@Nullable String message) {
|
|
+ sendSuccess(message, false);
|
|
+ }
|
|
+
|
|
+ public void sendSuccess(@Nullable String message, boolean broadcastToOps) {
|
|
+ if (message == null) {
|
|
+ return;
|
|
+ }
|
|
+ sendSuccess(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), broadcastToOps);
|
|
+ }
|
|
+
|
|
+ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message) {
|
|
+ sendSuccess(message, false);
|
|
+ }
|
|
+
|
|
+ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message, boolean broadcastToOps) {
|
|
+ if (message == null) {
|
|
+ return;
|
|
+ }
|
|
+ sendSuccess(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), broadcastToOps);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void sendSuccess(Component message, boolean broadcastToOps) {
|
|
if (this.source.acceptsSuccess() && !this.silent) {
|
|
this.source.sendSystemMessage(message);
|
|
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
|
|
index 87cc7562e4a166d078fe11b7f6980497fc0bd33e..08bed4f01a27162902aa63bb8d35a9159fdcfc4e 100644
|
|
--- a/src/main/java/net/minecraft/commands/Commands.java
|
|
+++ b/src/main/java/net/minecraft/commands/Commands.java
|
|
@@ -149,7 +149,7 @@ public class Commands {
|
|
DamageCommand.register(this.dispatcher, commandRegistryAccess);
|
|
DataCommands.register(this.dispatcher);
|
|
DataPackCommand.register(this.dispatcher);
|
|
- DebugCommand.register(this.dispatcher);
|
|
+ //DebugCommand.register(this.dispatcher); // Purpur
|
|
DefaultGameModeCommands.register(this.dispatcher);
|
|
DifficultyCommand.register(this.dispatcher);
|
|
EffectCommands.register(this.dispatcher, commandRegistryAccess);
|
|
@@ -222,6 +222,14 @@ public class Commands {
|
|
SetPlayerIdleTimeoutCommand.register(this.dispatcher);
|
|
StopCommand.register(this.dispatcher);
|
|
WhitelistCommand.register(this.dispatcher);
|
|
+ org.purpurmc.purpur.command.CreditsCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.UptimeCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.CompassCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.RamBarCommand.register(this.dispatcher); // Purpur
|
|
+ org.purpurmc.purpur.command.RamCommand.register(this.dispatcher); // Purpur
|
|
}
|
|
|
|
if (environment.includeIntegrated) {
|
|
@@ -309,9 +317,9 @@ public class Commands {
|
|
public int performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label) { // CraftBukkit
|
|
CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource();
|
|
|
|
- commandlistenerwrapper.getServer().getProfiler().push(() -> {
|
|
+ /*commandlistenerwrapper.getServer().getProfiler().push(() -> { // Purpur
|
|
return "/" + s;
|
|
- });
|
|
+ });*/ // Purpur
|
|
|
|
byte b0;
|
|
|
|
@@ -394,7 +402,7 @@ public class Commands {
|
|
b0 = 0;
|
|
}
|
|
} finally {
|
|
- commandlistenerwrapper.getServer().getProfiler().pop();
|
|
+ //commandlistenerwrapper.getServer().getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
return b0;
|
|
@@ -454,6 +462,7 @@ public class Commands {
|
|
private void runSync(ServerPlayer player, Collection<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
|
|
// Paper end - Async command map building
|
|
new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper
|
|
+ if (PlayerCommandSendEvent.getHandlerList().getRegisteredListeners().length > 0) { // Purpur - skip all this crap if there's nothing listening
|
|
PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit));
|
|
event.getPlayer().getServer().getPluginManager().callEvent(event);
|
|
|
|
@@ -464,6 +473,7 @@ public class Commands {
|
|
}
|
|
}
|
|
// CraftBukkit end
|
|
+ } // Purpur - skip event
|
|
player.connection.send(new ClientboundCommandsPacket(rootcommandnode));
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
index f25b9330e068c7d9e12cb57a7761cfef9ebaf7bc..7e66aaa960ce7b6dda7c064d4c6856cc4b368b58 100644
|
|
--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
@@ -200,10 +200,10 @@ public class EntitySelector {
|
|
|
|
if (this.playerName != null) {
|
|
entityplayer = source.getServer().getPlayerList().getPlayerByName(this.playerName);
|
|
- return (List) (entityplayer == null ? Collections.emptyList() : Lists.newArrayList(new ServerPlayer[]{entityplayer}));
|
|
+ return entityplayer == null || !canSee(source, entityplayer) ? Collections.emptyList() : Lists.newArrayList(entityplayer); // Purpur
|
|
} else if (this.entityUUID != null) {
|
|
entityplayer = source.getServer().getPlayerList().getPlayer(this.entityUUID);
|
|
- return (List) (entityplayer == null ? Collections.emptyList() : Lists.newArrayList(new ServerPlayer[]{entityplayer}));
|
|
+ return entityplayer == null || !canSee(source, entityplayer) ? Collections.emptyList() : Lists.newArrayList(entityplayer); // Purpur
|
|
} else {
|
|
Vec3 vec3d = (Vec3) this.position.apply(source.getPosition());
|
|
Predicate<Entity> predicate = this.getPredicate(vec3d);
|
|
@@ -213,7 +213,7 @@ public class EntitySelector {
|
|
ServerPlayer entityplayer1 = (ServerPlayer) source.getEntity();
|
|
|
|
if (predicate.test(entityplayer1)) {
|
|
- return Lists.newArrayList(new ServerPlayer[]{entityplayer1});
|
|
+ return !canSee(source, entityplayer1) ? Collections.emptyList() : Lists.newArrayList(entityplayer1); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -224,6 +224,7 @@ public class EntitySelector {
|
|
|
|
if (this.isWorldLimited()) {
|
|
object = source.getLevel().getPlayers(predicate, i);
|
|
+ ((List) object).removeIf(entityplayer3 -> !canSee(source, (ServerPlayer) entityplayer3)); // Purpur
|
|
} else {
|
|
object = Lists.newArrayList();
|
|
Iterator iterator = source.getServer().getPlayerList().getPlayers().iterator();
|
|
@@ -231,7 +232,7 @@ public class EntitySelector {
|
|
while (iterator.hasNext()) {
|
|
ServerPlayer entityplayer2 = (ServerPlayer) iterator.next();
|
|
|
|
- if (predicate.test(entityplayer2)) {
|
|
+ if (predicate.test(entityplayer2) && canSee(source, entityplayer2)) { // Purpur
|
|
((List) object).add(entityplayer2);
|
|
if (((List) object).size() >= i) {
|
|
return (List) object;
|
|
@@ -276,4 +277,10 @@ public class EntitySelector {
|
|
public static Component joinNames(List<? extends Entity> entities) {
|
|
return ComponentUtils.formatList(entities, Entity::getDisplayName);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ private boolean canSee(CommandSourceStack sender, ServerPlayer target) {
|
|
+ return !org.purpurmc.purpur.PurpurConfig.hideHiddenPlayersFromEntitySelector || !(sender.getEntity() instanceof ServerPlayer player) || player.getBukkitEntity().canSee(target.getBukkitEntity());
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
|
|
index b37e0ff164a894d2033fb94bbbc2f630a0e66bcd..ac335ec4f70830c7687ac4e0aa2a6cba9cb04ae1 100644
|
|
--- a/src/main/java/net/minecraft/core/BlockPos.java
|
|
+++ b/src/main/java/net/minecraft/core/BlockPos.java
|
|
@@ -41,6 +41,12 @@ public class BlockPos extends Vec3i {
|
|
private static final int X_OFFSET = 38;
|
|
// Paper end
|
|
|
|
+ // Purpur start
|
|
+ public BlockPos(net.minecraft.world.entity.Entity entity) {
|
|
+ super(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public BlockPos(int x, int y, int z) {
|
|
super(x, y, z);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/Direction.java b/src/main/java/net/minecraft/core/Direction.java
|
|
index 82bce6109d59cba30178a446f0ff129da6f3692f..eaa620ad86abfb151b43f697973cbc731e2e5e92 100644
|
|
--- a/src/main/java/net/minecraft/core/Direction.java
|
|
+++ b/src/main/java/net/minecraft/core/Direction.java
|
|
@@ -248,6 +248,12 @@ public enum Direction implements StringRepresentable {
|
|
case EAST:
|
|
var10000 = SOUTH;
|
|
break;
|
|
+ // Purpur start
|
|
+ case UP:
|
|
+ return UP;
|
|
+ case DOWN:
|
|
+ return DOWN;
|
|
+ // Purpur end
|
|
default:
|
|
throw new IllegalStateException("Unable to get Y-rotated facing of " + this);
|
|
}
|
|
@@ -360,6 +366,12 @@ public enum Direction implements StringRepresentable {
|
|
case EAST:
|
|
var10000 = NORTH;
|
|
break;
|
|
+ // Purpur start
|
|
+ case UP:
|
|
+ return UP;
|
|
+ case DOWN:
|
|
+ return DOWN;
|
|
+ // Purpur end
|
|
default:
|
|
throw new IllegalStateException("Unable to get CCW facing of " + this);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
index 88d18d18d69876c98e199acb647c6cca9448d55d..da9cc93f560269a00f0093ad76aba3a05eedb046 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
@@ -52,6 +52,7 @@ import net.minecraft.world.item.SpawnEggItem;
|
|
import net.minecraft.world.item.alchemy.PotionUtils;
|
|
import net.minecraft.world.item.alchemy.Potions;
|
|
import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.AnvilBlock;
|
|
import net.minecraft.world.level.block.BaseFireBlock;
|
|
import net.minecraft.world.level.block.BeehiveBlock;
|
|
import net.minecraft.world.level.block.Block;
|
|
@@ -1168,6 +1169,23 @@ public interface DispenseItemBehavior {
|
|
}
|
|
}
|
|
});
|
|
+ // Purpur start
|
|
+ DispenserBlock.registerBehavior(Items.ANVIL, (new OptionalDispenseItemBehavior() {
|
|
+ @Override
|
|
+ public ItemStack execute(BlockSource dispenser, ItemStack stack) {
|
|
+ Level level = dispenser.getLevel();
|
|
+ if (!level.purpurConfig.dispenserPlaceAnvils) return super.execute(dispenser, stack);
|
|
+ Direction facing = dispenser.getBlockState().getValue(DispenserBlock.FACING);
|
|
+ BlockPos pos = dispenser.getPos().relative(facing);
|
|
+ BlockState state = level.getBlockState(pos);
|
|
+ if (state.isAir()) {
|
|
+ level.setBlockAndUpdate(pos, Blocks.ANVIL.defaultBlockState().setValue(AnvilBlock.FACING, facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : facing.getClockWise()));
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ return stack;
|
|
+ }
|
|
+ }));
|
|
+ // Purpur end
|
|
}
|
|
|
|
static void setEntityPokingOutOfBlock(BlockSource pointer, Entity entity, Direction direction) {
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
|
index d1127d93a85a837933d0d73c24cacac4adc3a5b9..d9a6d273108165f59b995b1fd7748cb5c12b8b1f 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
|
@@ -107,7 +107,7 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
|
|
continue;
|
|
}
|
|
// CraftBukkit end
|
|
- ishearable.shear(SoundSource.BLOCKS);
|
|
+ ishearable.shear(SoundSource.BLOCKS, net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MOB_LOOTING, CraftItemStack.asNMSCopy(craftItem))); // Purpur
|
|
worldserver.gameEvent((Entity) null, GameEvent.SHEAR, blockposition);
|
|
return true;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
|
index f9e10bf048929886db3c414038d2c7e9f84226a6..323416311f14f5ad887f05183ad3b4921981aecd 100644
|
|
--- a/src/main/java/net/minecraft/network/Connection.java
|
|
+++ b/src/main/java/net/minecraft/network/Connection.java
|
|
@@ -572,11 +572,20 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper
|
|
private static int joinAttemptsThisTick; // Paper
|
|
private static int currTick; // Paper
|
|
+ private static int tickSecond; // Purpur
|
|
public void tick() {
|
|
this.flushQueue();
|
|
// Paper start
|
|
if (Connection.currTick != net.minecraft.server.MinecraftServer.currentTick) {
|
|
Connection.currTick = net.minecraft.server.MinecraftServer.currentTick;
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.maxJoinsPerSecond) {
|
|
+ if (++Connection.tickSecond > 20) {
|
|
+ Connection.tickSecond = 0;
|
|
+ Connection.joinAttemptsThisTick = 0;
|
|
+ }
|
|
+ } else
|
|
+ // Purpur end
|
|
Connection.joinAttemptsThisTick = 0;
|
|
}
|
|
// Paper end
|
|
diff --git a/src/main/java/net/minecraft/network/FriendlyByteBuf.java b/src/main/java/net/minecraft/network/FriendlyByteBuf.java
|
|
index 9938bb90bef84cf784f9a1ceb02a1a45aa8b48a1..1f4b64a5f812376c499c98cb4be62469bd0b7dbe 100644
|
|
--- a/src/main/java/net/minecraft/network/FriendlyByteBuf.java
|
|
+++ b/src/main/java/net/minecraft/network/FriendlyByteBuf.java
|
|
@@ -98,6 +98,8 @@ public class FriendlyByteBuf extends ByteBuf {
|
|
private static final int MAX_PUBLIC_KEY_LENGTH = 512;
|
|
private static final Gson GSON = new Gson();
|
|
|
|
+ public static boolean hasItemSerializeEvent = false; // Purpur
|
|
+
|
|
public FriendlyByteBuf(ByteBuf parent) {
|
|
this.source = parent;
|
|
}
|
|
@@ -679,6 +681,17 @@ public class FriendlyByteBuf extends ByteBuf {
|
|
this.writeBoolean(false);
|
|
} else {
|
|
this.writeBoolean(true);
|
|
+ // Purpur start
|
|
+ if (hasItemSerializeEvent) {
|
|
+ var event = new org.purpurmc.purpur.event.packet.NetworkItemSerializeEvent(stack.asBukkitCopy());
|
|
+ event.callEvent();
|
|
+ ItemStack newStack = ItemStack.fromBukkitCopy(event.getItemStack());
|
|
+ if (org.purpurmc.purpur.PurpurConfig.fixNetworkSerializedItemsInCreative && !ItemStack.matches(stack, newStack)) {
|
|
+ stack.save(newStack.getOrCreateTagElement("Purpur.OriginalItem"));
|
|
+ }
|
|
+ stack = newStack;
|
|
+ }
|
|
+ // Purpur end
|
|
Item item = stack.getItem();
|
|
|
|
this.writeId(BuiltInRegistries.ITEM, item);
|
|
diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
|
index d2f0a0755317f5fa9a1ccf7db346aa77fd287d80..03852e7d21d9470a4469676367463fefb38acdc6 100644
|
|
--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
|
+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
|
|
@@ -47,7 +47,7 @@ public class PacketUtils {
|
|
if (MinecraftServer.getServer().hasStopped() || (listener instanceof ServerGamePacketListenerImpl && ((ServerGamePacketListenerImpl) listener).processedDisconnect)) return; // CraftBukkit, MC-142590
|
|
if (listener.isAcceptingMessages()) {
|
|
co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings
|
|
- try (co.aikar.timings.Timing ignored = timing.startTiming()) { // Paper - timings
|
|
+ try { // Paper - timings // Purpur
|
|
packet.handle(listener);
|
|
} catch (Exception exception) {
|
|
if (listener.shouldPropagateHandlingExceptions()) {
|
|
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundPlayerCombatKillPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundPlayerCombatKillPacket.java
|
|
index 53b75f5737a910ffc5448cd9a85eae57f9c1488f..ea95873dd034779e56a8b924cd27f9375be05daf 100644
|
|
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundPlayerCombatKillPacket.java
|
|
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundPlayerCombatKillPacket.java
|
|
@@ -9,6 +9,7 @@ public class ClientboundPlayerCombatKillPacket implements Packet<ClientGamePacke
|
|
private final int playerId;
|
|
private final int killerId;
|
|
private final Component message;
|
|
+ public net.kyori.adventure.text.Component adventure$message; // Purpur
|
|
|
|
public ClientboundPlayerCombatKillPacket(CombatTracker damageTracker, Component message) {
|
|
this(damageTracker.getMob().getId(), damageTracker.getKillerId(), message);
|
|
@@ -30,6 +31,12 @@ public class ClientboundPlayerCombatKillPacket implements Packet<ClientGamePacke
|
|
public void write(FriendlyByteBuf buf) {
|
|
buf.writeVarInt(this.playerId);
|
|
buf.writeInt(this.killerId);
|
|
+ // Purpur start
|
|
+ if (this.adventure$message != null) {
|
|
+ buf.writeComponent(this.adventure$message);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
buf.writeComponent(this.message);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java
|
|
index 9ec6145fe04ec64bbee8ec6a837719caebdbc6f5..358d610ad020cada1bb83e393deeeaaec05a2791 100644
|
|
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java
|
|
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java
|
|
@@ -5,7 +5,7 @@ import net.minecraft.network.protocol.Packet;
|
|
|
|
public class ClientboundSetTimePacket implements Packet<ClientGamePacketListener> {
|
|
private final long gameTime;
|
|
- private final long dayTime;
|
|
+ private long dayTime; public void setDayTime(long dayTime) { this.dayTime = dayTime; } // Purpur
|
|
|
|
public ClientboundSetTimePacket(long time, long timeOfDay, boolean doDaylightCycle) {
|
|
this.gameTime = time;
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index a29d92b3f2658b63545b25092bb3a1fea46ca36b..beb05039926e1fb7a656dfcd0c503f82db67fc46 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -250,7 +250,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
private boolean allowFlight;
|
|
@Nullable
|
|
private String motd;
|
|
- @Nullable private net.kyori.adventure.text.Component cachedMotd; // Paper
|
|
+ private net.kyori.adventure.text.Component cachedMotd = net.kyori.adventure.text.Component.empty(); // Paper // Purpur
|
|
private int playerIdleTimeout;
|
|
public final long[] tickTimes;
|
|
// Paper start
|
|
@@ -296,6 +296,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public org.bukkit.command.RemoteConsoleCommandSender remoteConsole;
|
|
//public ConsoleReader reader; // Paper
|
|
public static int currentTick = 0; // Paper - Further improve tick loop
|
|
+ public static final long startTimeMillis = System.currentTimeMillis();
|
|
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
|
public int autosavePeriod;
|
|
public Commands vanillaCommandDispatcher;
|
|
@@ -305,10 +306,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public static final int TPS = 20;
|
|
public static final int TICK_TIME = 1000000000 / MinecraftServer.TPS;
|
|
private static final int SAMPLE_INTERVAL = 20; // Paper
|
|
- public final double[] recentTps = new double[ 3 ];
|
|
+ public final double[] recentTps = new double[ 4 ]; // Purpur
|
|
// Spigot end
|
|
public final io.papermc.paper.configuration.PaperConfigurations paperConfigurations;
|
|
public static long currentTickLong = 0L; // Paper
|
|
+ public boolean lagging = false; // Purpur
|
|
+ protected boolean upnp = false; // Purpur
|
|
|
|
public volatile Thread shutdownThread; // Paper
|
|
public volatile boolean abnormalExit = false; // Paper
|
|
@@ -340,13 +343,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public MinecraftServer(OptionSet options, WorldLoader.DataLoadContext worldLoader, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, Proxy proxy, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) {
|
|
super("Server");
|
|
SERVER = this; // Paper - better singleton
|
|
- this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
|
|
- this.profiler = this.metricsRecorder.getProfiler();
|
|
- this.onMetricsRecordingStopped = (methodprofilerresults) -> {
|
|
+ //this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; // Purpur
|
|
+ //this.profiler = this.metricsRecorder.getProfiler(); // Purpur
|
|
+ /*this.onMetricsRecordingStopped = (methodprofilerresults) -> { // Purpur
|
|
this.stopRecordingMetrics();
|
|
- };
|
|
- this.onMetricsRecordingFinished = (path) -> {
|
|
- };
|
|
+ };*/ // Purpur
|
|
+ //this.onMetricsRecordingFinished = (path) -> { // Purpur
|
|
+ //}; // Purpur
|
|
this.random = RandomSource.create();
|
|
this.port = -1;
|
|
this.levels = Maps.newLinkedHashMap();
|
|
@@ -927,7 +930,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
shutdownThread = Thread.currentThread();
|
|
org.spigotmc.WatchdogThread.doStop(); // Paper
|
|
if (!isSameThread()) {
|
|
- MinecraftServer.LOGGER.info("Stopping main thread (Ignore any thread death message you see! - DO NOT REPORT THREAD DEATH TO PAPER)");
|
|
+ MinecraftServer.LOGGER.info("Stopping main thread (Ignore any thread death message you see! - DO NOT REPORT THREAD DEATH TO PURPUR)"); // Purpur
|
|
while (this.getRunningThread().isAlive()) {
|
|
this.getRunningThread().stop();
|
|
try {
|
|
@@ -937,13 +940,21 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
// Paper end
|
|
// CraftBukkit end
|
|
- if (this.metricsRecorder.isRecording()) {
|
|
+ /*if (this.metricsRecorder.isRecording()) { // Purpur
|
|
this.cancelRecordingMetrics();
|
|
- }
|
|
+ }*/ // Purpur
|
|
|
|
MinecraftServer.LOGGER.info("Stopping server");
|
|
Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Shutdown and don't bother finishing
|
|
MinecraftTimings.stopServer(); // Paper
|
|
+ // Purpur start
|
|
+ if (upnp) {
|
|
+ if (dev.omega24.upnp4j.UPnP4J.close(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) {
|
|
+ LOGGER.info("[UPnP] Port {} closed", this.getPort());
|
|
+ } else {
|
|
+ LOGGER.error("[UPnP] Failed to close port {}", this.getPort());
|
|
+ }
|
|
+ }
|
|
// CraftBukkit start
|
|
if (this.server != null) {
|
|
this.server.disablePlugins();
|
|
@@ -1025,6 +1036,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
this.safeShutdown(waitForShutdown, false);
|
|
}
|
|
public void safeShutdown(boolean waitForShutdown, boolean isRestarting) {
|
|
+ org.purpurmc.purpur.task.BossBarTask.stopAll(); // Purpur
|
|
+ org.purpurmc.purpur.task.BeehiveTask.instance().unregister(); // Purpur
|
|
this.isRestarting = isRestarting;
|
|
this.hasLoggedStop = true; // Paper
|
|
if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
|
|
@@ -1051,6 +1064,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
private static final long MAX_CATCHUP_BUFFER = TICK_TIME * TPS * 60L;
|
|
private long lastTick = 0;
|
|
private long catchupTime = 0;
|
|
+ public final RollingAverage tps5s = new RollingAverage(5); // Purpur
|
|
public final RollingAverage tps1 = new RollingAverage(60);
|
|
public final RollingAverage tps5 = new RollingAverage(60 * 5);
|
|
public final RollingAverage tps15 = new RollingAverage(60 * 15);
|
|
@@ -1151,35 +1165,46 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
{
|
|
final long diff = curTime - tickSection;
|
|
java.math.BigDecimal currentTps = TPS_BASE.divide(new java.math.BigDecimal(diff), 30, java.math.RoundingMode.HALF_UP);
|
|
+ tps5s.add(currentTps, diff); // Purpur
|
|
tps1.add(currentTps, diff);
|
|
tps5.add(currentTps, diff);
|
|
tps15.add(currentTps, diff);
|
|
// Backwards compat with bad plugins
|
|
- this.recentTps[0] = tps1.getAverage();
|
|
- this.recentTps[1] = tps5.getAverage();
|
|
- this.recentTps[2] = tps15.getAverage();
|
|
+ // Purpur start
|
|
+ this.recentTps[0] = tps5s.getAverage();
|
|
+ this.recentTps[1] = tps1.getAverage();
|
|
+ this.recentTps[2] = tps5.getAverage();
|
|
+ this.recentTps[3] = tps15.getAverage();
|
|
+ // Purpur end
|
|
// Paper end
|
|
+ lagging = recentTps[0] < org.purpurmc.purpur.PurpurConfig.laggingThreshold; // Purpur
|
|
tickSection = curTime;
|
|
}
|
|
// Spigot end
|
|
|
|
- if (this.debugCommandProfilerDelayStart) {
|
|
+ /*if (this.debugCommandProfilerDelayStart) { // Purpur
|
|
this.debugCommandProfilerDelayStart = false;
|
|
this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
|
|
- }
|
|
+ }*/ // Purpur
|
|
|
|
//MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit // Paper - don't overwrite current tick time
|
|
lastTick = curTime;
|
|
this.nextTickTime += 50L;
|
|
- this.startMetricsRecordingTick();
|
|
- this.profiler.push("tick");
|
|
+ //this.startMetricsRecordingTick(); // Purpur
|
|
+ //this.profiler.push("tick"); // Purpur
|
|
this.tickServer(this::haveTime);
|
|
- this.profiler.popPush("nextTickWait");
|
|
+ //this.profiler.popPush("nextTickWait"); // Purpur
|
|
this.mayHaveDelayedTasks = true;
|
|
- this.delayedTasksMaxNextTickTime = Math.max(Util.getMillis() + 50L, this.nextTickTime);
|
|
+ // Purpur start - tps catchup
|
|
+ if (org.purpurmc.purpur.PurpurConfig.tpsCatchup) {
|
|
+ this.delayedTasksMaxNextTickTime = Math.max(Util.getMillis() + 50L, this.nextTickTime);
|
|
+ } else {
|
|
+ this.delayedTasksMaxNextTickTime = this.nextTickTime = curTime / 1000000L + 50L;
|
|
+ }
|
|
+ // Purpur end - tps catchup
|
|
this.waitUntilNextTick();
|
|
- this.profiler.pop();
|
|
- this.endMetricsRecordingTick();
|
|
+ //this.profiler.pop(); // Purpur
|
|
+ //this.endMetricsRecordingTick(); // Purpur
|
|
this.isReady = true;
|
|
JvmProfiler.INSTANCE.onServerTick(this.averageTickTime);
|
|
}
|
|
@@ -1340,7 +1365,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
public void doRunTask(TickTask ticktask) { // CraftBukkit - decompile error
|
|
- this.getProfiler().incrementCounter("runTask");
|
|
+ //this.getProfiler().incrementCounter("runTask"); // Purpur
|
|
super.doRunTask(ticktask);
|
|
}
|
|
|
|
@@ -1383,15 +1408,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public void onServerExit() {}
|
|
|
|
public void tickServer(BooleanSupplier shouldKeepTicking) {
|
|
- co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper
|
|
+ //co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper // Purpur
|
|
long i = Util.getNanos();
|
|
|
|
// Paper start - move oversleep into full server tick
|
|
- isOversleep = true;MinecraftTimings.serverOversleep.startTiming();
|
|
+ //isOversleep = true;MinecraftTimings.serverOversleep.startTiming(); // Purpur
|
|
this.managedBlock(() -> {
|
|
return !this.canOversleep();
|
|
});
|
|
- isOversleep = false;MinecraftTimings.serverOversleep.stopTiming();
|
|
+ //isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); // Purpur
|
|
// Paper end
|
|
new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper
|
|
|
|
@@ -1407,7 +1432,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
if (playerSaveInterval < 0) {
|
|
playerSaveInterval = autosavePeriod;
|
|
}
|
|
- this.profiler.push("save");
|
|
+ //this.profiler.push("save"); // Purpur
|
|
final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0;
|
|
try {
|
|
this.isSaving = true;
|
|
@@ -1422,20 +1447,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
} finally {
|
|
this.isSaving = false;
|
|
}
|
|
- this.profiler.pop();
|
|
+ //this.profiler.pop(); // Purpur
|
|
// Paper end
|
|
io.papermc.paper.util.CachedLists.reset(); // Paper
|
|
// Paper start - move executeAll() into full server tick timing
|
|
- try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) {
|
|
+ //try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) { // Purpur
|
|
this.runAllTasks();
|
|
- }
|
|
+ //} // Purpur
|
|
// Paper end
|
|
// Paper start
|
|
long endTime = System.nanoTime();
|
|
long remaining = (TICK_TIME - (endTime - lastTick)) - catchupTime;
|
|
new com.destroystokyo.paper.event.server.ServerTickEndEvent(this.tickCount, ((double)(endTime - lastTick) / 1000000D), remaining).callEvent();
|
|
// Paper end
|
|
- this.profiler.push("tallying");
|
|
+ //this.profiler.push("tallying"); // Purpur
|
|
long j = this.tickTimes[this.tickCount % 100] = Util.getNanos() - i;
|
|
|
|
this.averageTickTime = this.averageTickTime * 0.8F + (float) j / 1000000.0F * 0.19999999F;
|
|
@@ -1447,9 +1472,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
tickTimes60s.add(this.tickCount, j);
|
|
// Paper end
|
|
this.frameTimer.logFrameDuration(k - i);
|
|
- this.profiler.pop();
|
|
+ //this.profiler.pop(); // Purpur
|
|
org.spigotmc.WatchdogThread.tick(); // Spigot
|
|
- co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper
|
|
+ //co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper // Purpur
|
|
}
|
|
|
|
private ServerStatus buildServerStatus() {
|
|
@@ -1481,26 +1506,26 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
public void tickChildren(BooleanSupplier shouldKeepTicking) {
|
|
- MinecraftTimings.bukkitSchedulerTimer.startTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.bukkitSchedulerTimer.startTiming(); // Spigot // Paper // Purpur
|
|
this.server.getScheduler().mainThreadHeartbeat(this.tickCount); // CraftBukkit
|
|
- MinecraftTimings.bukkitSchedulerTimer.stopTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.bukkitSchedulerTimer.stopTiming(); // Spigot // Paper // Purpur
|
|
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
|
|
- this.profiler.push("commandFunctions");
|
|
- MinecraftTimings.commandFunctionsTimer.startTiming(); // Spigot // Paper
|
|
+ //this.profiler.push("commandFunctions"); // Purpur
|
|
+ //MinecraftTimings.commandFunctionsTimer.startTiming(); // Spigot // Paper // Purpur
|
|
this.getFunctions().tick();
|
|
- MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot // Paper
|
|
- this.profiler.popPush("levels");
|
|
+ //MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot // Paper // Purpur
|
|
+ //this.profiler.popPush("levels"); // Purpur
|
|
//Iterator iterator = this.getAllLevels().iterator(); // Paper - moved down
|
|
|
|
// CraftBukkit start
|
|
// Run tasks that are waiting on processing
|
|
- MinecraftTimings.processQueueTimer.startTiming(); // Spigot
|
|
+ //MinecraftTimings.processQueueTimer.startTiming(); // Spigot // Purpur
|
|
while (!this.processQueue.isEmpty()) {
|
|
this.processQueue.remove().run();
|
|
}
|
|
- MinecraftTimings.processQueueTimer.stopTiming(); // Spigot
|
|
+ //MinecraftTimings.processQueueTimer.stopTiming(); // Spigot // Purpur
|
|
|
|
- MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper // Purpur
|
|
// Send time updates to everyone, it will get the right time from the world the player is in.
|
|
// Paper start - optimize time updates
|
|
for (final ServerLevel world : this.getAllLevels()) {
|
|
@@ -1509,7 +1534,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
long worldTime = world.getGameTime();
|
|
final ClientboundSetTimePacket worldPacket = new ClientboundSetTimePacket(worldTime, dayTime, doDaylight);
|
|
for (Player entityhuman : world.players()) {
|
|
- if (!(entityhuman instanceof ServerPlayer) || (tickCount + entityhuman.getId()) % 20 != 0) {
|
|
+ if (!(entityhuman instanceof ServerPlayer) || (!world.isForceTime() && (tickCount + entityhuman.getId()) % 20 != 0)) { // Purpur
|
|
continue;
|
|
}
|
|
ServerPlayer entityplayer = (ServerPlayer) entityhuman;
|
|
@@ -1520,38 +1545,40 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
}
|
|
// Paper end
|
|
- MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot // Paper // Purpur
|
|
|
|
this.isIteratingOverLevels = true; // Paper
|
|
+ net.minecraft.network.FriendlyByteBuf.hasItemSerializeEvent = org.purpurmc.purpur.event.packet.NetworkItemSerializeEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur
|
|
Iterator iterator = this.getAllLevels().iterator(); // Paper - move down
|
|
while (iterator.hasNext()) {
|
|
ServerLevel worldserver = (ServerLevel) iterator.next();
|
|
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
|
|
net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
|
|
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
|
|
+ worldserver.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur
|
|
|
|
- this.profiler.push(() -> {
|
|
+ /*this.profiler.push(() -> { // Purpur
|
|
return worldserver + " " + worldserver.dimension().location();
|
|
- });
|
|
+ });*/ // Purpur
|
|
/* Drop global time updates
|
|
if (this.tickCount % 20 == 0) {
|
|
- this.profiler.push("timeSync");
|
|
+ //this.profiler.push("timeSync"); // Purpur
|
|
this.synchronizeTime(worldserver);
|
|
- this.profiler.pop();
|
|
+ //this.profiler.pop(); // Purpur
|
|
}
|
|
// CraftBukkit end */
|
|
|
|
- this.profiler.push("tick");
|
|
+ //this.profiler.push("tick"); // Purpur
|
|
|
|
try {
|
|
- worldserver.timings.doTick.startTiming(); // Spigot
|
|
+ //worldserver.timings.doTick.startTiming(); // Spigot // Purpur
|
|
worldserver.tick(shouldKeepTicking);
|
|
// Paper start
|
|
for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) {
|
|
regionManager.recalculateRegions();
|
|
}
|
|
// Paper end
|
|
- worldserver.timings.doTick.stopTiming(); // Spigot
|
|
+ //worldserver.timings.doTick.stopTiming(); // Spigot // Purpur
|
|
} catch (Throwable throwable) {
|
|
// Spigot Start
|
|
CrashReport crashreport;
|
|
@@ -1567,33 +1594,33 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
throw new ReportedException(crashreport);
|
|
}
|
|
|
|
- this.profiler.pop();
|
|
- this.profiler.pop();
|
|
+ //this.profiler.pop(); // Purpur
|
|
+ //this.profiler.pop(); // Purpur
|
|
worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions
|
|
}
|
|
this.isIteratingOverLevels = false; // Paper
|
|
|
|
- this.profiler.popPush("connection");
|
|
- MinecraftTimings.connectionTimer.startTiming(); // Spigot
|
|
+ //this.profiler.popPush("connection"); // Purpur
|
|
+ //MinecraftTimings.connectionTimer.startTiming(); // Spigot // Purpur
|
|
this.getConnection().tick();
|
|
- MinecraftTimings.connectionTimer.stopTiming(); // Spigot
|
|
- this.profiler.popPush("players");
|
|
- MinecraftTimings.playerListTimer.startTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.connectionTimer.stopTiming(); // Spigot // Purpur
|
|
+ //this.profiler.popPush("players"); // Purpur
|
|
+ //MinecraftTimings.playerListTimer.startTiming(); // Spigot // Paper // Purpur
|
|
this.playerList.tick();
|
|
- MinecraftTimings.playerListTimer.stopTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.playerListTimer.stopTiming(); // Spigot // Paper // Purpur
|
|
if (SharedConstants.IS_RUNNING_IN_IDE) {
|
|
GameTestTicker.SINGLETON.tick();
|
|
}
|
|
|
|
- this.profiler.popPush("server gui refresh");
|
|
+ //this.profiler.popPush("server gui refresh"); // Purpur
|
|
|
|
- MinecraftTimings.tickablesTimer.startTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.tickablesTimer.startTiming(); // Spigot // Paper // Purpur
|
|
for (int i = 0; i < this.tickables.size(); ++i) {
|
|
((Runnable) this.tickables.get(i)).run();
|
|
}
|
|
- MinecraftTimings.tickablesTimer.stopTiming(); // Spigot // Paper
|
|
+ //MinecraftTimings.tickablesTimer.stopTiming(); // Spigot // Paper // Purpur
|
|
|
|
- this.profiler.pop();
|
|
+ //this.profiler.pop(); // Purpur
|
|
}
|
|
|
|
private void synchronizeTime(ServerLevel world) {
|
|
@@ -1601,7 +1628,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
public void forceTimeSynchronization() {
|
|
- this.profiler.push("timeSync");
|
|
+ //this.profiler.push("timeSync"); // Purpur
|
|
Iterator iterator = this.getAllLevels().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -1610,7 +1637,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
this.synchronizeTime(worldserver);
|
|
}
|
|
|
|
- this.profiler.pop();
|
|
+ //this.profiler.pop(); // Purpur
|
|
}
|
|
|
|
public boolean isNetherEnabled() {
|
|
@@ -1684,7 +1711,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
@DontObfuscate
|
|
public String getServerModName() {
|
|
- return "Pufferfish"; // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla!
|
|
+ return org.purpurmc.purpur.PurpurConfig.serverModName; // Purpur - Purpur > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla!
|
|
}
|
|
|
|
public SystemReport fillSystemReport(SystemReport details) {
|
|
@@ -1877,17 +1904,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
public net.kyori.adventure.text.Component getComponentMotd() {
|
|
- net.kyori.adventure.text.Component component = cachedMotd;
|
|
- if (this.motd != null && this.cachedMotd == null) {
|
|
- component = cachedMotd = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(this.motd);
|
|
- }
|
|
-
|
|
- return component != null ? component : net.kyori.adventure.text.Component.empty();
|
|
+ return this.cachedMotd; // Purpur
|
|
}
|
|
|
|
public void setMotd(String motd) {
|
|
this.motd = motd;
|
|
- this.cachedMotd = null; // Paper
|
|
+ this.cachedMotd = motd == null ? net.kyori.adventure.text.Component.empty() : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(motd); // Paper // Purpur
|
|
}
|
|
|
|
public boolean isStopped() {
|
|
@@ -2269,7 +2291,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
public ProfilerFiller getProfiler() {
|
|
- if (gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE;
|
|
+ if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE;
|
|
return this.profiler;
|
|
}
|
|
|
|
@@ -2509,7 +2531,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
// CraftBukkit end
|
|
|
|
private void startMetricsRecordingTick() {
|
|
- if (this.willStartRecordingMetrics) {
|
|
+ if (false && this.willStartRecordingMetrics) { // Purpur
|
|
this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()), Util.timeSource, Util.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> {
|
|
this.executeBlocking(() -> {
|
|
this.saveDebugReport(path.resolve("server"));
|
|
@@ -2519,40 +2541,40 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
this.willStartRecordingMetrics = false;
|
|
}
|
|
|
|
- this.profiler = SingleTickProfiler.decorateFiller(this.metricsRecorder.getProfiler(), SingleTickProfiler.createTickProfiler("Server"));
|
|
- this.metricsRecorder.startTick();
|
|
- this.profiler.startTick();
|
|
+ //this.profiler = SingleTickProfiler.decorateFiller(this.metricsRecorder.getProfiler(), SingleTickProfiler.createTickProfiler("Server")); // Purpur
|
|
+ //this.metricsRecorder.startTick(); // Purpur
|
|
+ //this.profiler.startTick(); // Purpur
|
|
}
|
|
|
|
private void endMetricsRecordingTick() {
|
|
- this.profiler.endTick();
|
|
- this.metricsRecorder.endTick();
|
|
+ //this.profiler.endTick(); // Purpur
|
|
+ //this.metricsRecorder.endTick(); // Purpur
|
|
}
|
|
|
|
public boolean isRecordingMetrics() {
|
|
- return this.metricsRecorder.isRecording();
|
|
+ return false; //this.metricsRecorder.isRecording(); // Purpur
|
|
}
|
|
|
|
public void startRecordingMetrics(Consumer<ProfileResults> resultConsumer, Consumer<Path> dumpConsumer) {
|
|
- this.onMetricsRecordingStopped = (methodprofilerresults) -> {
|
|
+ /*this.onMetricsRecordingStopped = (methodprofilerresults) -> { // Purpur
|
|
this.stopRecordingMetrics();
|
|
resultConsumer.accept(methodprofilerresults);
|
|
};
|
|
this.onMetricsRecordingFinished = dumpConsumer;
|
|
- this.willStartRecordingMetrics = true;
|
|
+ this.willStartRecordingMetrics = true;*/ // Purpur
|
|
}
|
|
|
|
public void stopRecordingMetrics() {
|
|
- this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
|
|
+ //this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; // Purpur
|
|
}
|
|
|
|
public void finishRecordingMetrics() {
|
|
- this.metricsRecorder.end();
|
|
+ //this.metricsRecorder.end(); // Purpur
|
|
}
|
|
|
|
public void cancelRecordingMetrics() {
|
|
- this.metricsRecorder.cancel();
|
|
- this.profiler = this.metricsRecorder.getProfiler();
|
|
+ //this.metricsRecorder.cancel(); // Purpur
|
|
+ //this.profiler = this.metricsRecorder.getProfiler(); // Purpur
|
|
}
|
|
|
|
public Path getWorldPath(LevelResource worldSavePath) {
|
|
@@ -2601,15 +2623,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
public boolean isTimeProfilerRunning() {
|
|
- return this.debugCommandProfilerDelayStart || this.debugCommandProfiler != null;
|
|
+ return false; //this.debugCommandProfilerDelayStart || this.debugCommandProfiler != null; // Purpur
|
|
}
|
|
|
|
public void startTimeProfiler() {
|
|
- this.debugCommandProfilerDelayStart = true;
|
|
+ //this.debugCommandProfilerDelayStart = true; // Purpur
|
|
}
|
|
|
|
public ProfileResults stopTimeProfiler() {
|
|
- if (this.debugCommandProfiler == null) {
|
|
+ if (true || this.debugCommandProfiler == null) { // Purpur
|
|
return EmptyProfileResults.EMPTY;
|
|
} else {
|
|
ProfileResults methodprofilerresults = this.debugCommandProfiler.stop(Util.getNanos(), this.tickCount);
|
|
@@ -2641,6 +2663,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Chat Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
|
|
|
|
public ChatDecorator getChatDecorator() {
|
|
+ // Purpur start
|
|
+ return this.chatDecorator;
|
|
+ }
|
|
+ public void setChatDecorator(ChatDecorator chatDecorator) {
|
|
+ this.chatDecorator = chatDecorator;
|
|
+ }
|
|
+ private ChatDecorator chatDecorator = getPaperHardcodedChatDecorator();
|
|
+ public ChatDecorator getPaperHardcodedChatDecorator() {
|
|
+ // Purpur end
|
|
// Paper start - moved to ChatPreviewProcessor
|
|
return ChatDecorator.create((sender, commandSourceStack, message) -> {
|
|
final io.papermc.paper.adventure.ChatDecorationProcessor processor = new io.papermc.paper.adventure.ChatDecorationProcessor(this, sender, commandSourceStack, message);
|
|
@@ -2775,7 +2806,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
return;
|
|
}
|
|
|
|
- co.aikar.timings.MinecraftTimings.midTickChunkTasks.startTiming();
|
|
+ //co.aikar.timings.MinecraftTimings.midTickChunkTasks.startTiming(); // Purpur
|
|
try {
|
|
for (;;) {
|
|
boolean moreTasks = this.tickMidTickTasks();
|
|
@@ -2802,7 +2833,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
}
|
|
} finally {
|
|
- co.aikar.timings.MinecraftTimings.midTickChunkTasks.stopTiming();
|
|
+ //co.aikar.timings.MinecraftTimings.midTickChunkTasks.stopTiming(); // Purpur
|
|
}
|
|
}
|
|
// Paper end - execute chunk tasks mid tick
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
index 26888ebd38280de92e41dd8006d2b24e874afe26..3919d9c193abcfd8b97dfb0ceb38638440f60fbe 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java
|
|
@@ -147,6 +147,7 @@ public class PlayerAdvancements {
|
|
if (advancement == null) {
|
|
// CraftBukkit start
|
|
if (entry.getKey().getNamespace().equals("minecraft")) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressIgnoredAdvancementWarnings) // Purpur
|
|
PlayerAdvancements.LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", entry.getKey(), this.playerSavePath);
|
|
}
|
|
// CraftBukkit end
|
|
@@ -249,6 +250,7 @@ public class PlayerAdvancements {
|
|
advancement.getRewards().grant(this.player);
|
|
// Paper start - Add Adventure message to PlayerAdvancementDoneEvent
|
|
if (message != null && this.player.level.getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur
|
|
this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), false);
|
|
// Paper end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/ServerFunctionManager.java b/src/main/java/net/minecraft/server/ServerFunctionManager.java
|
|
index 6483a1d461904a0584b6808b2f86ac7329bba963..8645313e646e6d5278e285f7449447761d7aae29 100644
|
|
--- a/src/main/java/net/minecraft/server/ServerFunctionManager.java
|
|
+++ b/src/main/java/net/minecraft/server/ServerFunctionManager.java
|
|
@@ -57,10 +57,10 @@ public class ServerFunctionManager {
|
|
}
|
|
|
|
private void executeTagFunctions(Collection<CommandFunction> functions, ResourceLocation label) {
|
|
- ProfilerFiller gameprofilerfiller = this.server.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.server.getProfiler(); // Purpur
|
|
|
|
Objects.requireNonNull(label);
|
|
- gameprofilerfiller.push(label::toString);
|
|
+ //gameprofilerfiller.push(label::toString); // Purpur
|
|
Iterator iterator = functions.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -69,7 +69,7 @@ public class ServerFunctionManager {
|
|
this.execute(customfunction, this.getGameLoopSender());
|
|
}
|
|
|
|
- this.server.getProfiler().pop();
|
|
+ //this.server.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
public int execute(CommandFunction function, CommandSourceStack source) {
|
|
@@ -88,7 +88,7 @@ public class ServerFunctionManager {
|
|
} else {
|
|
int i;
|
|
|
|
- try (co.aikar.timings.Timing timing = function.getTiming().startTiming()) { // Paper
|
|
+ try /*(co.aikar.timings.Timing timing = function.getTiming().startTiming())*/ { // Paper // Purpur
|
|
this.context = new ServerFunctionManager.ExecutionContext(tracer);
|
|
i = this.context.runTopCommand(function, source);
|
|
} finally {
|
|
@@ -177,10 +177,10 @@ public class ServerFunctionManager {
|
|
|
|
try {
|
|
ServerFunctionManager.QueuedCommand customfunctiondata_b = (ServerFunctionManager.QueuedCommand) this.commandQueue.removeFirst();
|
|
- ProfilerFiller gameprofilerfiller = ServerFunctionManager.this.server.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = ServerFunctionManager.this.server.getProfiler(); // Purpur
|
|
|
|
Objects.requireNonNull(customfunctiondata_b);
|
|
- gameprofilerfiller.push(customfunctiondata_b::toString);
|
|
+ //gameprofilerfiller.push(customfunctiondata_b::toString); // Purpur
|
|
this.depth = customfunctiondata_b.depth;
|
|
customfunctiondata_b.execute(ServerFunctionManager.this, this.commandQueue, i, this.tracer);
|
|
if (!this.nestedCalls.isEmpty()) {
|
|
@@ -192,7 +192,7 @@ public class ServerFunctionManager {
|
|
this.nestedCalls.clear();
|
|
}
|
|
} finally {
|
|
- ServerFunctionManager.this.server.getProfiler().pop();
|
|
+ //ServerFunctionManager.this.server.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
++j;
|
|
diff --git a/src/main/java/net/minecraft/server/commands/EnchantCommand.java b/src/main/java/net/minecraft/server/commands/EnchantCommand.java
|
|
index e639c0ec642910e66b1d68ae0b9208ef58d91fce..24c4ad919eeb9c5e15572ee32b0895c993ac6735 100644
|
|
--- a/src/main/java/net/minecraft/server/commands/EnchantCommand.java
|
|
+++ b/src/main/java/net/minecraft/server/commands/EnchantCommand.java
|
|
@@ -48,7 +48,7 @@ public class EnchantCommand {
|
|
|
|
private static int enchant(CommandSourceStack source, Collection<? extends Entity> targets, Holder<Enchantment> enchantment, int level) throws CommandSyntaxException {
|
|
Enchantment enchantment2 = enchantment.value();
|
|
- if (level > enchantment2.getMaxLevel()) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && level > enchantment2.getMaxLevel()) { // Purpur
|
|
throw ERROR_LEVEL_TOO_HIGH.create(level, enchantment2.getMaxLevel());
|
|
} else {
|
|
int i = 0;
|
|
@@ -58,7 +58,7 @@ public class EnchantCommand {
|
|
LivingEntity livingEntity = (LivingEntity)entity;
|
|
ItemStack itemStack = livingEntity.getMainHandItem();
|
|
if (!itemStack.isEmpty()) {
|
|
- if (enchantment2.canEnchant(itemStack) && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantments(itemStack).keySet(), enchantment2)) {
|
|
+ if ((enchantment2.canEnchant(itemStack) && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantments(itemStack).keySet(), enchantment2)) || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && !itemStack.hasEnchantment(enchantment2))) { // Purpur
|
|
itemStack.enchant(enchantment2, level);
|
|
++i;
|
|
} else if (targets.size() == 1) {
|
|
diff --git a/src/main/java/net/minecraft/server/commands/GameModeCommand.java b/src/main/java/net/minecraft/server/commands/GameModeCommand.java
|
|
index 27c0aaf123c3e945eb24e8a3892bd8ac42115733..85e1c1d6eb4472baa958b4f482791e8479dfcbf0 100644
|
|
--- a/src/main/java/net/minecraft/server/commands/GameModeCommand.java
|
|
+++ b/src/main/java/net/minecraft/server/commands/GameModeCommand.java
|
|
@@ -41,6 +41,18 @@ public class GameModeCommand {
|
|
}
|
|
|
|
private static int setMode(CommandContext<CommandSourceStack> context, Collection<ServerPlayer> targets, GameType gameMode) {
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.commandGamemodeRequiresPermission) {
|
|
+ String gamemode = gameMode.getName();
|
|
+ CommandSourceStack sender = context.getSource();
|
|
+ if (!sender.testPermission(2, "minecraft.command.gamemode." + gamemode)) {
|
|
+ return 0;
|
|
+ }
|
|
+ if (sender.getEntity() instanceof ServerPlayer player && (targets.size() > 1 || !targets.contains(player)) && !sender.testPermission(2, "minecraft.command.gamemode." + gamemode + ".other")) {
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
int i = 0;
|
|
|
|
for(ServerPlayer serverPlayer : targets) {
|
|
diff --git a/src/main/java/net/minecraft/server/commands/GiveCommand.java b/src/main/java/net/minecraft/server/commands/GiveCommand.java
|
|
index ee7d29d85c8b024c9b23cba8ecd4192aa7e8aa7b..7a44bac6e66bc5f5fe14a45a5e7c78c940fb1efb 100644
|
|
--- a/src/main/java/net/minecraft/server/commands/GiveCommand.java
|
|
+++ b/src/main/java/net/minecraft/server/commands/GiveCommand.java
|
|
@@ -58,6 +58,7 @@ public class GiveCommand {
|
|
boolean flag = entityplayer.getInventory().add(itemstack);
|
|
ItemEntity entityitem;
|
|
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableGiveCommandDrops) continue; // Purpur - add config option for toggling give command dropping
|
|
if (flag && itemstack.isEmpty()) {
|
|
itemstack.setCount(1);
|
|
entityitem = entityplayer.drop(itemstack, false, false, false); // SPIGOT-2942: Add boolean to call event
|
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
index ad4fdbdcf09f30d10e61ccf47f8fb9ce6bd92e73..6ecc75621867390738e804e06ac284524664473d 100644
|
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
@@ -99,6 +99,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
return;
|
|
}
|
|
// Paper start - Use TerminalConsoleAppender
|
|
+ if (DedicatedServer.this.gui == null || System.console() != null) // Purpur - has no GUI or has console (did not double-click)
|
|
new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start();
|
|
/*
|
|
jline.console.ConsoleReader bufferedreader = reader;
|
|
@@ -218,9 +219,19 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
thread.start(); // Paper - start console thread after MinecraftServer.console & PaperConfig are initialized
|
|
io.papermc.paper.command.PaperCommands.registerCommands(this);
|
|
com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics();
|
|
+ // Purpur start
|
|
+ try {
|
|
+ org.purpurmc.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings"));
|
|
+ } catch (Exception e) {
|
|
+ DedicatedServer.LOGGER.error("Unable to load server configuration", e);
|
|
+ return false;
|
|
+ }
|
|
+ org.purpurmc.purpur.PurpurConfig.registerCommands();
|
|
+ // Purpur end
|
|
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
|
|
io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider
|
|
// Paper end
|
|
+ gg.pufferfish.pufferfish.PufferfishConfig.pufferfishFile = (java.io.File) options.valueOf("pufferfish-settings"); // Purpur
|
|
gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish
|
|
gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish
|
|
|
|
@@ -269,6 +280,30 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
DedicatedServer.LOGGER.warn("Perhaps a server is already running on that port?");
|
|
return false;
|
|
}
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.useUPnP) {
|
|
+ LOGGER.info("[UPnP] Attempting to start UPnP port forwarding service...");
|
|
+ if (dev.omega24.upnp4j.UPnP4J.isUPnPAvailable()) {
|
|
+ if (dev.omega24.upnp4j.UPnP4J.isOpen(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) {
|
|
+ this.upnp = false;
|
|
+ LOGGER.info("[UPnP] Port {} is already open", this.getPort());
|
|
+ } else if (dev.omega24.upnp4j.UPnP4J.open(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) {
|
|
+ this.upnp = true;
|
|
+ LOGGER.info("[UPnP] Successfully opened port {}", this.getPort());
|
|
+ } else {
|
|
+ this.upnp = false;
|
|
+ LOGGER.info("[UPnP] Failed to open port {}", this.getPort());
|
|
+ }
|
|
+
|
|
+ if (upnp) {
|
|
+ LOGGER.info("[UPnP] {}:{}", dev.omega24.upnp4j.UPnP4J.getExternalIP(), this.getPort());
|
|
+ }
|
|
+ } else {
|
|
+ this.upnp = false;
|
|
+ LOGGER.error("[UPnP] Service is unavailable");
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
// CraftBukkit start
|
|
// this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // Spigot - moved up
|
|
@@ -342,6 +377,9 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
}
|
|
|
|
if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) mobSpawnExecutor.start(); // Pufferfish
|
|
+ org.purpurmc.purpur.task.BossBarTask.startAll(); // Purpur
|
|
+ org.purpurmc.purpur.task.BeehiveTask.instance().register(); // Purpur
|
|
+
|
|
return true;
|
|
}
|
|
}
|
|
@@ -488,7 +526,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
}
|
|
|
|
public void handleConsoleInputs() {
|
|
- MinecraftTimings.serverCommandTimer.startTiming(); // Spigot
|
|
+ //MinecraftTimings.serverCommandTimer.startTiming(); // Spigot // Purpur
|
|
// Paper start - use proper queue
|
|
ConsoleInput servercommand;
|
|
while ((servercommand = this.serverCommandQueue.poll()) != null) {
|
|
@@ -505,7 +543,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
// CraftBukkit end
|
|
}
|
|
|
|
- MinecraftTimings.serverCommandTimer.stopTiming(); // Spigot
|
|
+ //MinecraftTimings.serverCommandTimer.stopTiming(); // Spigot // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
|
index 818289e831e3dad29345c43265e2efd7689bc500..1ea3012995c738c67b31e997c138f824f9e69ba1 100644
|
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
|
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
|
@@ -58,6 +58,7 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
|
|
public final boolean onlineMode = this.get("online-mode", true);
|
|
public final boolean preventProxyConnections = this.get("prevent-proxy-connections", false);
|
|
public final String serverIp = this.get("server-ip", "");
|
|
+ public final String serverName = this.get("server-name", "Unknown Server"); // Purpur
|
|
public final boolean spawnAnimals = this.get("spawn-animals", true);
|
|
public final boolean spawnNpcs = this.get("spawn-npcs", true);
|
|
public final boolean pvp = this.get("pvp", true);
|
|
diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
|
index c07918aa1ed2469ad7a76a0add60ab648ff7f421..56cf3d5b8e365ce6b1ec88464d9079d774206755 100644
|
|
--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
|
+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
|
@@ -43,6 +43,11 @@ public class MinecraftServerGui extends JComponent {
|
|
private Thread logAppenderThread;
|
|
private final Collection<Runnable> finalizers = Lists.newArrayList();
|
|
final AtomicBoolean isClosing = new AtomicBoolean();
|
|
+ // Purpur start
|
|
+ private final CommandHistory history = new CommandHistory();
|
|
+ private String currentCommand = "";
|
|
+ private int historyIndex = 0;
|
|
+ // Purpur end
|
|
|
|
public static MinecraftServerGui showFrameFor(final DedicatedServer server) {
|
|
try {
|
|
@@ -51,7 +56,7 @@ public class MinecraftServerGui extends JComponent {
|
|
;
|
|
}
|
|
|
|
- final JFrame jframe = new JFrame("Minecraft server");
|
|
+ final JFrame jframe = new JFrame("Purpur Minecraft server"); // Purpur
|
|
final MinecraftServerGui servergui = new MinecraftServerGui(server);
|
|
|
|
jframe.setDefaultCloseOperation(2);
|
|
@@ -59,7 +64,7 @@ public class MinecraftServerGui extends JComponent {
|
|
jframe.pack();
|
|
jframe.setLocationRelativeTo((Component) null);
|
|
jframe.setVisible(true);
|
|
- jframe.setName("Minecraft server"); // Paper
|
|
+ jframe.setName("Purpur Minecraft server"); // Paper // Purpur
|
|
|
|
// Paper start - Add logo as frame image
|
|
try {
|
|
@@ -71,7 +76,7 @@ public class MinecraftServerGui extends JComponent {
|
|
jframe.addWindowListener(new WindowAdapter() {
|
|
public void windowClosing(WindowEvent windowevent) {
|
|
if (!servergui.isClosing.getAndSet(true)) {
|
|
- jframe.setTitle("Minecraft server - shutting down!");
|
|
+ jframe.setTitle("Purpur Minecraft server - shutting down!"); // Purpur
|
|
server.halt(true);
|
|
servergui.runFinalizers();
|
|
}
|
|
@@ -125,7 +130,7 @@ public class MinecraftServerGui extends JComponent {
|
|
|
|
private JComponent buildChatPanel() {
|
|
JPanel jpanel = new JPanel(new BorderLayout());
|
|
- JTextArea jtextarea = new JTextArea();
|
|
+ org.purpurmc.purpur.gui.JColorTextPane jtextarea = new org.purpurmc.purpur.gui.JColorTextPane(); // Purpur
|
|
JScrollPane jscrollpane = new JScrollPane(jtextarea, 22, 30);
|
|
|
|
jtextarea.setEditable(false);
|
|
@@ -137,10 +142,43 @@ public class MinecraftServerGui extends JComponent {
|
|
|
|
if (!s.isEmpty()) {
|
|
this.server.handleConsoleInput(s, this.server.createCommandSourceStack());
|
|
+ // Purpur start
|
|
+ history.add(s);
|
|
+ historyIndex = -1;
|
|
+ // Purpur end
|
|
}
|
|
|
|
jtextfield.setText("");
|
|
});
|
|
+ // Purpur start
|
|
+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("UP"), "up");
|
|
+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("DOWN"), "down");
|
|
+ jtextfield.getActionMap().put("up", new javax.swing.AbstractAction() {
|
|
+ @Override
|
|
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
|
|
+ if (historyIndex < 0) {
|
|
+ currentCommand = jtextfield.getText();
|
|
+ }
|
|
+ if (historyIndex < history.size() - 1) {
|
|
+ jtextfield.setText(history.get(historyIndex));
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ jtextfield.getActionMap().put("down", new javax.swing.AbstractAction() {
|
|
+ @Override
|
|
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
|
|
+ if (historyIndex >= 0) {
|
|
+ if (historyIndex == 0) {
|
|
+ --historyIndex;
|
|
+ jtextfield.setText(currentCommand);
|
|
+ } else {
|
|
+ --historyIndex;
|
|
+ jtextfield.setText(history.get(historyIndex));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
jtextarea.addFocusListener(new FocusAdapter() {
|
|
public void focusGained(FocusEvent focusevent) {}
|
|
});
|
|
@@ -176,7 +214,7 @@ public class MinecraftServerGui extends JComponent {
|
|
}
|
|
|
|
private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?[m|K]"); // CraftBukkit
|
|
- public void print(JTextArea textArea, JScrollPane scrollPane, String message) {
|
|
+ public void print(org.purpurmc.purpur.gui.JColorTextPane textArea, JScrollPane scrollPane, String message) { // Purpur
|
|
if (!SwingUtilities.isEventDispatchThread()) {
|
|
SwingUtilities.invokeLater(() -> {
|
|
this.print(textArea, scrollPane, message);
|
|
@@ -190,11 +228,14 @@ public class MinecraftServerGui extends JComponent {
|
|
flag = (double) jscrollbar.getValue() + jscrollbar.getSize().getHeight() + (double) (MinecraftServerGui.MONOSPACED.getSize() * 4) > (double) jscrollbar.getMaximum();
|
|
}
|
|
|
|
+ /* // Purpur
|
|
try {
|
|
document.insertString(document.getLength(), MinecraftServerGui.ANSI.matcher(message).replaceAll(""), (AttributeSet) null); // CraftBukkit
|
|
} catch (BadLocationException badlocationexception) {
|
|
;
|
|
}
|
|
+ */ // Purpur
|
|
+ textArea.append(message); // Purpur
|
|
|
|
if (flag) {
|
|
jscrollbar.setValue(Integer.MAX_VALUE);
|
|
@@ -202,4 +243,16 @@ public class MinecraftServerGui extends JComponent {
|
|
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public static class CommandHistory extends java.util.LinkedList<String> {
|
|
+ @Override
|
|
+ public boolean add(String command) {
|
|
+ if (size() > 1000) {
|
|
+ remove();
|
|
+ }
|
|
+ return super.offerFirst(command);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 3ce4dbf4eed442d89d6bbc8e4c6a000172041da5..57fdef8b16e1ed9a4693356144b4685bbcea285c 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -621,20 +621,20 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
|
|
protected void tick(BooleanSupplier shouldKeepTicking) {
|
|
- ProfilerFiller gameprofilerfiller = this.level.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur
|
|
|
|
- try (Timing ignored = this.level.timings.poiUnload.startTiming()) { // Paper
|
|
- gameprofilerfiller.push("poi");
|
|
+ //try (Timing ignored = this.level.timings.poiUnload.startTiming()) { // Paper // Purpur
|
|
+ //gameprofilerfiller.push("poi"); // Purpur
|
|
this.poiManager.tick(shouldKeepTicking);
|
|
- } // Paper
|
|
- gameprofilerfiller.popPush("chunk_unload");
|
|
+ //} // Paper // Purpur
|
|
+ //gameprofilerfiller.popPush("chunk_unload"); // Purpur
|
|
if (!this.level.noSave()) {
|
|
- try (Timing ignored = this.level.timings.chunkUnload.startTiming()) { // Paper
|
|
+ //try (Timing ignored = this.level.timings.chunkUnload.startTiming()) { // Paper // Purpur
|
|
this.processUnloads(shouldKeepTicking);
|
|
- } // Paper
|
|
+ //} // Paper // Purpur
|
|
}
|
|
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
}
|
|
|
|
public boolean hasWork() {
|
|
@@ -998,7 +998,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
return this.anyPlayerCloseEnoughForSpawning(this.getUpdatingChunkIfPresent(chunkcoordintpair.toLong()), chunkcoordintpair, reducedRange);
|
|
}
|
|
|
|
- final boolean anyPlayerCloseEnoughForSpawning(ChunkHolder playerchunk, ChunkPos chunkcoordintpair, boolean reducedRange) {
|
|
+ public final boolean anyPlayerCloseEnoughForSpawning(ChunkHolder playerchunk, ChunkPos chunkcoordintpair, boolean reducedRange) { // Purpur - package -> public
|
|
// this function is so hot that removing the map lookup call can have an order of magnitude impact on its performance
|
|
// tested and confirmed via System.nanoTime()
|
|
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInRange = reducedRange ? playerchunk.playersInMobSpawnRange : playerchunk.playersInChunkTickRange;
|
|
@@ -1253,24 +1253,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
// Paper start - optimised tracker
|
|
private final void processTrackQueue() {
|
|
- this.level.timings.tracker1.startTiming();
|
|
+ //this.level.timings.tracker1.startTiming(); // Purpur
|
|
try {
|
|
for (TrackedEntity tracker : this.entityMap.values()) {
|
|
// update tracker entry
|
|
tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
|
|
}
|
|
} finally {
|
|
- this.level.timings.tracker1.stopTiming();
|
|
+ //this.level.timings.tracker1.stopTiming(); // Purpur
|
|
}
|
|
|
|
|
|
- this.level.timings.tracker2.startTiming();
|
|
+ //this.level.timings.tracker2.startTiming(); // Purpur
|
|
try {
|
|
for (TrackedEntity tracker : this.entityMap.values()) {
|
|
tracker.serverEntity.sendChanges();
|
|
}
|
|
} finally {
|
|
- this.level.timings.tracker2.stopTiming();
|
|
+ //this.level.timings.tracker2.stopTiming(); // Purpur
|
|
}
|
|
}
|
|
// Paper end - optimised tracker
|
|
@@ -1285,7 +1285,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
List<ServerPlayer> list = Lists.newArrayList();
|
|
List<ServerPlayer> list1 = this.level.players();
|
|
ObjectIterator objectiterator = this.entityMap.values().iterator();
|
|
- level.timings.tracker1.startTiming(); // Paper
|
|
+ //level.timings.tracker1.startTiming(); // Paper // Purpur
|
|
|
|
ChunkMap.TrackedEntity playerchunkmap_entitytracker;
|
|
|
|
@@ -1310,17 +1310,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
playerchunkmap_entitytracker.serverEntity.sendChanges();
|
|
}
|
|
}
|
|
- level.timings.tracker1.stopTiming(); // Paper
|
|
+ //level.timings.tracker1.stopTiming(); // Paper // Purpur
|
|
|
|
if (!list.isEmpty()) {
|
|
objectiterator = this.entityMap.values().iterator();
|
|
|
|
- level.timings.tracker2.startTiming(); // Paper
|
|
+ //level.timings.tracker2.startTiming(); // Paper // Purpur
|
|
while (objectiterator.hasNext()) {
|
|
playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
|
|
playerchunkmap_entitytracker.updatePlayers(list);
|
|
}
|
|
- level.timings.tracker2.stopTiming(); // Paper
|
|
+ //level.timings.tracker2.stopTiming(); // Paper // Purpur
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index c6f5d6756fa0e068a462d9c0ded12e0771abba37..0ae45cf5a084fd412305e8b2f5dabe608b4eb1c1 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -431,16 +431,16 @@ public class ServerChunkCache extends ChunkSource {
|
|
return ifLoaded;
|
|
}
|
|
// Paper end
|
|
- ProfilerFiller gameprofilerfiller = this.level.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur
|
|
|
|
- gameprofilerfiller.incrementCounter("getChunk");
|
|
+ //gameprofilerfiller.incrementCounter("getChunk"); // Purpur
|
|
long k = ChunkPos.asLong(x, z);
|
|
|
|
ChunkAccess ichunkaccess;
|
|
|
|
// Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
|
|
|
|
- gameprofilerfiller.incrementCounter("getChunkCacheMiss");
|
|
+ //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur
|
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
|
|
ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
|
|
|
|
@@ -450,10 +450,10 @@ public class ServerChunkCache extends ChunkSource {
|
|
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
|
|
// Paper end
|
|
com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
|
|
- this.level.timings.syncChunkLoad.startTiming(); // Paper
|
|
+ //this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur
|
|
chunkproviderserver_b.managedBlock(completablefuture::isDone);
|
|
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
|
|
- this.level.timings.syncChunkLoad.stopTiming(); // Paper
|
|
+ //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur
|
|
} // Paper
|
|
ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
|
|
return ichunkaccess1;
|
|
@@ -601,17 +601,17 @@ public class ServerChunkCache extends ChunkSource {
|
|
|
|
public void save(boolean flush) {
|
|
this.runDistanceManagerUpdates();
|
|
- try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
|
|
+ //try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings // Purpur
|
|
this.chunkMap.saveAllChunks(flush);
|
|
- } // Paper - Timings
|
|
+ //} // Paper - Timings // Purpur
|
|
}
|
|
|
|
// Paper start - duplicate save, but call incremental
|
|
public void saveIncrementally() {
|
|
this.runDistanceManagerUpdates();
|
|
- try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
|
|
+ //try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings // Purpur
|
|
this.chunkMap.saveIncrementally();
|
|
- } // Paper - Timings
|
|
+ //} // Paper - Timings // Purpur
|
|
}
|
|
// Paper end
|
|
|
|
@@ -628,36 +628,36 @@ public class ServerChunkCache extends ChunkSource {
|
|
// CraftBukkit start - modelled on below
|
|
public void purgeUnload() {
|
|
if (true) return; // Paper - tickets will be removed later, this behavior isn't really well accounted for by the chunk system
|
|
- this.level.getProfiler().push("purge");
|
|
+ //this.level.getProfiler().push("purge"); // Purpur
|
|
this.distanceManager.purgeStaleTickets();
|
|
this.runDistanceManagerUpdates();
|
|
- this.level.getProfiler().popPush("unload");
|
|
+ //this.level.getProfiler().popPush("unload"); // Purpur
|
|
this.chunkMap.tick(() -> true);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
this.clearCache();
|
|
}
|
|
// CraftBukkit end
|
|
|
|
@Override
|
|
public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) {
|
|
- this.level.getProfiler().push("purge");
|
|
- this.level.timings.doChunkMap.startTiming(); // Spigot
|
|
+ //this.level.getProfiler().push("purge"); // Purpur
|
|
+ //this.level.timings.doChunkMap.startTiming(); // Spigot // Purpur
|
|
this.distanceManager.purgeStaleTickets();
|
|
this.runDistanceManagerUpdates();
|
|
- this.level.timings.doChunkMap.stopTiming(); // Spigot
|
|
- this.level.getProfiler().popPush("chunks");
|
|
+ //this.level.timings.doChunkMap.stopTiming(); // Spigot // Purpur
|
|
+ //this.level.getProfiler().popPush("chunks"); // Purpur
|
|
if (tickChunks) {
|
|
- this.level.timings.chunks.startTiming(); // Paper - timings
|
|
+ //this.level.timings.chunks.startTiming(); // Paper - timings // Purpur
|
|
this.chunkMap.playerChunkManager.tick(); // Paper - this is mostly is to account for view distance changes
|
|
this.tickChunks();
|
|
- this.level.timings.chunks.stopTiming(); // Paper - timings
|
|
+ //this.level.timings.chunks.stopTiming(); // Paper - timings // Purpur
|
|
}
|
|
|
|
- this.level.timings.doChunkUnload.startTiming(); // Spigot
|
|
- this.level.getProfiler().popPush("unload");
|
|
+ //this.level.timings.doChunkUnload.startTiming(); // Spigot // Purpur
|
|
+ //this.level.getProfiler().popPush("unload"); // Purpur
|
|
this.chunkMap.tick(shouldKeepTicking);
|
|
- this.level.timings.doChunkUnload.stopTiming(); // Spigot
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.timings.doChunkUnload.stopTiming(); // Spigot // Purpur
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
this.clearCache();
|
|
}
|
|
|
|
@@ -703,15 +703,15 @@ public class ServerChunkCache extends ChunkSource {
|
|
}
|
|
// Paper end - optimize isOutisdeRange
|
|
LevelData worlddata = this.level.getLevelData();
|
|
- ProfilerFiller gameprofilerfiller = this.level.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur
|
|
|
|
- gameprofilerfiller.push("pollingChunks");
|
|
+ //gameprofilerfiller.push("pollingChunks"); // Purpur
|
|
this.level.resetIceAndSnowTick(); // Pufferfish - reset ice & snow tick random
|
|
int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
|
|
boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
|
|
|
|
- gameprofilerfiller.push("naturalSpawnCount");
|
|
- this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
|
|
+ //gameprofilerfiller.push("naturalSpawnCount"); // Purpur
|
|
+ //this.level.timings.countNaturalMobs.startTiming(); // Paper - timings // Purpur
|
|
int l = this.distanceManager.getNaturalSpawnChunkCount();
|
|
// Paper start - per player mob spawning
|
|
NaturalSpawner.SpawnState spawnercreature_d; // moved down
|
|
@@ -732,16 +732,16 @@ public class ServerChunkCache extends ChunkSource {
|
|
// Pufferfish end
|
|
}
|
|
// Paper end
|
|
- this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
|
|
+ //this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings // Purpur
|
|
|
|
//this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously
|
|
- gameprofilerfiller.popPush("filteringLoadedChunks");
|
|
+ //gameprofilerfiller.popPush("filteringLoadedChunks"); // Purpur
|
|
// Paper - moved down
|
|
- this.level.timings.chunkTicks.startTiming(); // Paper
|
|
+ //this.level.timings.chunkTicks.startTiming(); // Paper // Purpur
|
|
|
|
// Paper - moved down
|
|
|
|
- gameprofilerfiller.popPush("spawnAndTick");
|
|
+ //gameprofilerfiller.popPush("spawnAndTick"); // Purpur
|
|
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
|
|
|
|
// Paper - only shuffle if per-player mob spawning is disabled
|
|
@@ -791,17 +791,17 @@ public class ServerChunkCache extends ChunkSource {
|
|
}
|
|
}
|
|
// Paper end - optimise chunk tick iteration
|
|
- this.level.timings.chunkTicks.stopTiming(); // Paper
|
|
- gameprofilerfiller.popPush("customSpawners");
|
|
+ //this.level.timings.chunkTicks.stopTiming(); // Paper // Purpur
|
|
+ //gameprofilerfiller.popPush("customSpawners"); // Purpur
|
|
if (flag2) {
|
|
- try (co.aikar.timings.Timing ignored = this.level.timings.miscMobSpawning.startTiming()) { // Paper - timings
|
|
+ //try (co.aikar.timings.Timing ignored = this.level.timings.miscMobSpawning.startTiming()) { // Paper - timings // Purpur
|
|
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
|
|
- } // Paper - timings
|
|
+ //} // Paper - timings // Purpur
|
|
}
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
// Paper start - use set of chunks requiring updates, rather than iterating every single one loaded
|
|
- gameprofilerfiller.popPush("broadcast");
|
|
- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
|
|
+ //gameprofilerfiller.popPush("broadcast"); // Purpur
|
|
+ //this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur
|
|
if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
|
|
ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
|
|
this.chunkMap.needsChangeBroadcasting.clear();
|
|
@@ -813,8 +813,8 @@ public class ServerChunkCache extends ChunkSource {
|
|
}
|
|
}
|
|
}
|
|
- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
|
- gameprofilerfiller.pop();
|
|
+ //this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing // Purpur
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
// Paper end - use set of chunks requiring updates, rather than iterating every single one loaded
|
|
// Paper start - controlled flush for entity tracker packets
|
|
List<net.minecraft.network.Connection> disabledFlushes = new java.util.ArrayList<>(this.level.players.size());
|
|
@@ -1029,7 +1029,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
|
|
@Override
|
|
protected void doRunTask(Runnable task) {
|
|
- ServerChunkCache.this.level.getProfiler().incrementCounter("runTask");
|
|
+ //ServerChunkCache.this.level.getProfiler().incrementCounter("runTask"); // Purpur
|
|
super.doRunTask(task);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
index d5cb594f0b17ec9dc1a19cdb99bba553e70171be..6afee2a744a3498d4a0eee35f77cde444f73d12c 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
@@ -72,7 +72,7 @@ public class ServerEntity {
|
|
@Nullable
|
|
private List<SynchedEntityData.DataValue<?>> trackedDataValues;
|
|
// CraftBukkit start
|
|
- final Set<ServerPlayerConnection> trackedPlayers; // Paper - private -> package
|
|
+ public final Set<ServerPlayerConnection> trackedPlayers; // Paper - private -> package // Purpur - package -> public
|
|
|
|
public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer<Packet<?>> consumer, Set<ServerPlayerConnection> trackedPlayers) {
|
|
this.trackedPlayers = trackedPlayers;
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index 3ee5c3c17d450dce54e051dc53c9df44d9b3dc1b..86b8485c0fb1dc5cd79c9e24546dc74459822a48 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -212,6 +212,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
private final StructureManager structureManager;
|
|
private final StructureCheck structureCheck;
|
|
private final boolean tickTime;
|
|
+ private double preciseTime; // Purpur
|
|
+ private boolean forceTime; // Purpur
|
|
public long lastMidTickExecuteFailure; // Paper - execute chunk tasks mid tick
|
|
|
|
// CraftBukkit start
|
|
@@ -220,6 +222,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
public boolean hasPhysicsEvent = true; // Paper
|
|
public boolean hasEntityMoveEvent = false; // Paper
|
|
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
|
|
+ public boolean hasRidableMoveEvent = false; // Purpur
|
|
public static Throwable getAddToWorldStackTrace(Entity entity) {
|
|
final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date());
|
|
io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
|
|
@@ -542,7 +545,24 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
this.dragonParts = new Int2ObjectOpenHashMap();
|
|
this.tickTime = flag1;
|
|
this.server = minecraftserver;
|
|
- this.customSpawners = list;
|
|
+ // Purpur start - enable/disable MobSpawners per world
|
|
+ this.customSpawners = Lists.newArrayList();
|
|
+ if (purpurConfig.phantomSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.level.levelgen.PhantomSpawner());
|
|
+ }
|
|
+ if (purpurConfig.patrolSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.level.levelgen.PatrolSpawner());
|
|
+ }
|
|
+ if (purpurConfig.catSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.entity.npc.CatSpawner());
|
|
+ }
|
|
+ if (purpurConfig.villageSiegeSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.entity.ai.village.VillageSiege());
|
|
+ }
|
|
+ if (purpurConfig.villagerTraderSpawning) {
|
|
+ customSpawners.add(new net.minecraft.world.entity.npc.WanderingTraderSpawner(iworlddataserver));
|
|
+ }
|
|
+ // Purpur end
|
|
this.serverLevelData = iworlddataserver;
|
|
ChunkGenerator chunkgenerator = worlddimension.generator();
|
|
// CraftBukkit start
|
|
@@ -605,6 +625,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
this.chunkTaskScheduler = new io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler(this, io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.workerThreads); // Paper - rewrite chunk system
|
|
this.entityLookup = new io.papermc.paper.chunk.system.entity.EntityLookup(this, new EntityCallbacks()); // Paper - rewrite chunk system
|
|
+ this.preciseTime = this.serverLevelData.getDayTime(); // Purpur
|
|
}
|
|
|
|
public void setWeatherParameters(int clearDuration, int rainDuration, boolean raining, boolean thundering) {
|
|
@@ -633,17 +654,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
}
|
|
// Paper end - optimise checkDespawn
|
|
- ProfilerFiller gameprofilerfiller = this.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur
|
|
|
|
this.handlingTick = true;
|
|
- gameprofilerfiller.push("world border");
|
|
+ //gameprofilerfiller.push("world border"); // Purpur
|
|
this.getWorldBorder().tick();
|
|
- gameprofilerfiller.popPush("weather");
|
|
+ //gameprofilerfiller.popPush("weather"); // Purpur
|
|
this.advanceWeatherCycle();
|
|
int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
|
|
long j;
|
|
|
|
- if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) {
|
|
+ if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) {
|
|
// CraftBukkit start
|
|
j = this.levelData.getDayTime() + 24000L;
|
|
TimeSkipEvent event = new TimeSkipEvent(this.getWorld(), TimeSkipEvent.SkipReason.NIGHT_SKIP, (j - j % 24000L) - this.getDayTime());
|
|
@@ -665,32 +686,32 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
this.updateSkyBrightness();
|
|
this.tickTime();
|
|
- gameprofilerfiller.popPush("tickPending");
|
|
- timings.scheduledBlocks.startTiming(); // Paper
|
|
+ //gameprofilerfiller.popPush("tickPending"); // Purpur
|
|
+ //timings.scheduledBlocks.startTiming(); // Paper // Purpur
|
|
if (!this.isDebug()) {
|
|
j = this.getGameTime();
|
|
- gameprofilerfiller.push("blockTicks");
|
|
+ //gameprofilerfiller.push("blockTicks"); // Purpur
|
|
this.blockTicks.tick(j, 65536, this::tickBlock);
|
|
- gameprofilerfiller.popPush("fluidTicks");
|
|
+ //gameprofilerfiller.popPush("fluidTicks"); // Purpur
|
|
this.fluidTicks.tick(j, 65536, this::tickFluid);
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
}
|
|
- timings.scheduledBlocks.stopTiming(); // Paper
|
|
+ //timings.scheduledBlocks.stopTiming(); // Paper // Purpur
|
|
|
|
- gameprofilerfiller.popPush("raid");
|
|
- this.timings.raids.startTiming(); // Paper - timings
|
|
+ //gameprofilerfiller.popPush("raid"); // Purpur
|
|
+ //this.timings.raids.startTiming(); // Paper - timings // Purpur
|
|
this.raids.tick();
|
|
- this.timings.raids.stopTiming(); // Paper - timings
|
|
- gameprofilerfiller.popPush("chunkSource");
|
|
- this.timings.chunkProviderTick.startTiming(); // Paper - timings
|
|
+ //this.timings.raids.stopTiming(); // Paper - timings // Purpur
|
|
+ //gameprofilerfiller.popPush("chunkSource"); // Purpur
|
|
+ //this.timings.chunkProviderTick.startTiming(); // Paper - timings // Purpur
|
|
this.getChunkSource().tick(shouldKeepTicking, true);
|
|
- this.timings.chunkProviderTick.stopTiming(); // Paper - timings
|
|
- gameprofilerfiller.popPush("blockEvents");
|
|
- timings.doSounds.startTiming(); // Spigot
|
|
+ //this.timings.chunkProviderTick.stopTiming(); // Paper - timings // Purpur
|
|
+ //gameprofilerfiller.popPush("blockEvents"); // Purpur
|
|
+ //timings.doSounds.startTiming(); // Spigot // Purpur
|
|
this.runBlockEvents();
|
|
- timings.doSounds.stopTiming(); // Spigot
|
|
+ //timings.doSounds.stopTiming(); // Spigot // Purpur
|
|
this.handlingTick = false;
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players
|
|
|
|
if (flag) {
|
|
@@ -698,25 +719,25 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
|
|
if (flag || this.emptyTime++ < 300) {
|
|
- gameprofilerfiller.push("entities");
|
|
- timings.tickEntities.startTiming(); // Spigot
|
|
+ //gameprofilerfiller.push("entities"); // Purpur
|
|
+ //timings.tickEntities.startTiming(); // Spigot // Purpur
|
|
if (this.dragonFight != null) {
|
|
- gameprofilerfiller.push("dragonFight");
|
|
+ //gameprofilerfiller.push("dragonFight"); // Purpur
|
|
this.dragonFight.tick();
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
}
|
|
|
|
org.spigotmc.ActivationRange.activateEntities(this); // Spigot
|
|
- timings.entityTick.startTiming(); // Spigot
|
|
+ //timings.entityTick.startTiming(); // Spigot // Purpur
|
|
this.entityTickList.forEach((entity) -> {
|
|
entity.activatedPriorityReset = false; // Pufferfish - DAB
|
|
if (!entity.isRemoved()) {
|
|
if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed
|
|
entity.discard();
|
|
} else {
|
|
- gameprofilerfiller.push("checkDespawn");
|
|
+ //gameprofilerfiller.push("checkDespawn"); // Purpur
|
|
entity.checkDespawn();
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list
|
|
Entity entity1 = entity.getVehicle();
|
|
|
|
@@ -728,7 +749,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
entity.stopRiding();
|
|
}
|
|
|
|
- gameprofilerfiller.push("tick");
|
|
+ //gameprofilerfiller.push("tick"); // Purpur
|
|
// Pufferfish start - copied from this.guardEntityTick
|
|
try {
|
|
this.tickNonPassenger(entity); // Pufferfish - changed
|
|
@@ -743,20 +764,19 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
// Paper end
|
|
}
|
|
// Pufferfish end
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
}
|
|
}
|
|
}
|
|
});
|
|
- timings.entityTick.stopTiming(); // Spigot
|
|
- timings.tickEntities.stopTiming(); // Spigot
|
|
- gameprofilerfiller.pop();
|
|
+ //timings.entityTick.stopTiming(); // Spigot // Purpur
|
|
+ //timings.tickEntities.stopTiming(); // Spigot // Purpur
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
this.tickBlockEntities();
|
|
}
|
|
|
|
- gameprofilerfiller.push("entityManagement");
|
|
+ //gameprofilerfiller.push("entityManagement"); // Purpur
|
|
//this.entityManager.tick(); // Paper - rewrite chunk system
|
|
- gameprofilerfiller.pop();
|
|
}
|
|
|
|
@Override
|
|
@@ -774,6 +794,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
this.serverLevelData.setGameTime(i);
|
|
this.serverLevelData.getScheduledEvents().tick(this.server, i);
|
|
if (this.levelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
|
|
+ // Purpur start
|
|
+ int incrementTicks = isDay() ? this.purpurConfig.daytimeTicks : this.purpurConfig.nighttimeTicks;
|
|
+ if (incrementTicks != 12000) {
|
|
+ this.preciseTime += 12000 / (double) incrementTicks;
|
|
+ this.setDayTime(this.preciseTime);
|
|
+ } else
|
|
+ // Purpur end
|
|
this.setDayTime(this.levelData.getDayTime() + 1L);
|
|
}
|
|
|
|
@@ -782,7 +809,21 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
public void setDayTime(long timeOfDay) {
|
|
this.serverLevelData.setDayTime(timeOfDay);
|
|
+ // Purpur start
|
|
+ this.preciseTime = timeOfDay;
|
|
+ this.forceTime = false;
|
|
}
|
|
+ public void setDayTime(double i) {
|
|
+ this.serverLevelData.setDayTime((long) i);
|
|
+ this.forceTime = true;
|
|
+ // Purpur end
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ public boolean isForceTime() {
|
|
+ return this.forceTime;
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
public void tickCustomSpawners(boolean spawnMonsters, boolean spawnAnimals) {
|
|
Iterator iterator = this.customSpawners.iterator();
|
|
@@ -807,7 +848,7 @@ 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(); // Pufferfish - moved to super
|
|
+ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(this.random.nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - moved to super // Purpur - dont break ABI
|
|
// Paper end
|
|
|
|
private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.randomTickRandom.nextInt(16); } // Pufferfish
|
|
@@ -817,9 +858,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
boolean flag = this.isRaining();
|
|
int j = chunkcoordintpair.getMinBlockX();
|
|
int k = chunkcoordintpair.getMinBlockZ();
|
|
- ProfilerFiller gameprofilerfiller = this.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur
|
|
|
|
- gameprofilerfiller.push("thunder");
|
|
+ //gameprofilerfiller.push("thunder"); // Purpur
|
|
final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change
|
|
|
|
if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*this.random.nextInt(this.spigotConfig.thunderChance) == 0 &&*/ chunk.shouldDoLightning(this.random)) { // Spigot // Paper - disable thunder // Pufferfish - replace random with shouldDoLightning
|
|
@@ -829,10 +870,18 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper
|
|
|
|
if (flag1) {
|
|
- SkeletonHorse entityhorseskeleton = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this);
|
|
+ // Purpur start
|
|
+ net.minecraft.world.entity.animal.horse.AbstractHorse entityhorseskeleton;
|
|
+ if (purpurConfig.zombieHorseSpawnChance > 0D && random.nextDouble() <= purpurConfig.zombieHorseSpawnChance) {
|
|
+ entityhorseskeleton = EntityType.ZOMBIE_HORSE.create(this);
|
|
+ } else {
|
|
+ entityhorseskeleton = EntityType.SKELETON_HORSE.create(this);
|
|
+ if (entityhorseskeleton != null) ((SkeletonHorse) entityhorseskeleton).setTrap(true);
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
if (entityhorseskeleton != null) {
|
|
- entityhorseskeleton.setTrap(true);
|
|
+ //entityhorseskeleton.setTrap(true); // Purpur - moved up
|
|
entityhorseskeleton.setAge(0);
|
|
entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
|
|
this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
|
|
@@ -849,7 +898,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
}
|
|
|
|
- gameprofilerfiller.popPush("iceandsnow");
|
|
+ //gameprofilerfiller.popPush("iceandsnow"); // Purpur
|
|
int l;
|
|
|
|
if (!this.paperConfig().environment.disableIceAndSnow && (this.currentIceAndSnowTick++ & 15) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking // Pufferfish - optimize further random ticking
|
|
@@ -900,8 +949,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
|
|
// Paper start - optimise random block ticking
|
|
- gameprofilerfiller.popPush("randomTick");
|
|
- timings.chunkTicksBlocks.startTiming(); // Paper
|
|
+ //gameprofilerfiller.popPush("randomTick"); // Purpur
|
|
+ //timings.chunkTicksBlocks.startTiming(); // Paper // Purpur
|
|
if (randomTickSpeed > 0) {
|
|
LevelChunkSection[] sections = chunk.getSections();
|
|
int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this);
|
|
@@ -935,8 +984,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
}
|
|
// Paper end - optimise random block ticking
|
|
- timings.chunkTicksBlocks.stopTiming(); // Paper
|
|
- gameprofilerfiller.pop();
|
|
+ //timings.chunkTicksBlocks.stopTiming(); // Paper // Purpur
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
}
|
|
|
|
public Optional<BlockPos> findLightningRod(BlockPos pos) {
|
|
@@ -944,7 +993,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
return holder.is(PoiTypes.LIGHTNING_ROD);
|
|
}, (blockposition1) -> {
|
|
return blockposition1.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockposition1.getX(), blockposition1.getZ()) - 1;
|
|
- }, pos, 128, PoiManager.Occupancy.ANY);
|
|
+ }, pos, org.purpurmc.purpur.PurpurConfig.lightningRodRange, PoiManager.Occupancy.ANY);
|
|
|
|
return optional.map((blockposition1) -> {
|
|
return blockposition1.above(1);
|
|
@@ -993,11 +1042,27 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
if (this.canSleepThroughNights()) {
|
|
if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) {
|
|
int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
|
|
- MutableComponent ichatmutablecomponent;
|
|
+ Component ichatmutablecomponent;
|
|
|
|
if (this.sleepStatus.areEnoughSleeping(i)) {
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.isBlank()) {
|
|
+ return;
|
|
+ }
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.equalsIgnoreCase("default")) {
|
|
+ ichatmutablecomponent = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepSkippingNight));
|
|
+ } else
|
|
ichatmutablecomponent = Component.translatable("sleep.skipping_night");
|
|
} else {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.isBlank()) {
|
|
+ return;
|
|
+ }
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.equalsIgnoreCase("default")) {
|
|
+ ichatmutablecomponent = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent,
|
|
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("count", Integer.toString(this.sleepStatus.amountSleeping())),
|
|
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("total", Integer.toString(this.sleepStatus.sleepersNeeded(i)))));
|
|
+ } else
|
|
+ // Purpur end
|
|
ichatmutablecomponent = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i));
|
|
}
|
|
|
|
@@ -1136,6 +1201,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
private void resetWeatherCycle() {
|
|
// CraftBukkit start
|
|
+ if (this.purpurConfig.rainStopsAfterSleep) // Purpur
|
|
this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - when passing the night
|
|
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
|
// Not that everyone ever manages to get the whole server to sleep at the same time....
|
|
@@ -1143,6 +1209,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
this.serverLevelData.setRainTime(0);
|
|
}
|
|
// CraftBukkit end
|
|
+ if (this.purpurConfig.thunderStopsAfterSleep) // Purpur
|
|
this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - when passing the night
|
|
// CraftBukkit start
|
|
// If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
|
@@ -1210,24 +1277,24 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
// Spigot end
|
|
// Paper start- timings
|
|
final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity);
|
|
- timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper
|
|
- try {
|
|
+ //timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper // Purpur
|
|
+ //try { // Purpur
|
|
// Paper end - timings
|
|
entity.setOldPosAndRot();
|
|
- ProfilerFiller gameprofilerfiller = this.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur
|
|
|
|
++entity.tickCount;
|
|
- this.getProfiler().push(() -> {
|
|
+ /*this.getProfiler().push(() -> { // Purpur
|
|
return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
|
|
- });
|
|
- gameprofilerfiller.incrementCounter("tickNonPassenger");
|
|
+ });*/ // Purpur
|
|
+ //gameprofilerfiller.incrementCounter("tickNonPassenger"); // Purpur
|
|
if (isActive) { // Paper - EAR 2
|
|
TimingHistory.activatedEntityTicks++;
|
|
entity.tick();
|
|
entity.postTick(); // CraftBukkit
|
|
} else { entity.inactiveTick(); } // Paper - EAR 2
|
|
- this.getProfiler().pop();
|
|
- } finally { timer.stopTiming(); } // Paper - timings
|
|
+ //this.getProfiler().pop(); // Purpur
|
|
+ //} finally { timer.stopTiming(); } // Paper - timings // Purpur
|
|
Iterator iterator = entity.getPassengers().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -1250,17 +1317,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
if (passenger instanceof Player || this.entityTickList.contains(passenger)) {
|
|
// Paper - EAR 2
|
|
final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(passenger);
|
|
- co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper
|
|
- try {
|
|
+ //co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper // Purpur
|
|
+ //try { // Purpur
|
|
// Paper end
|
|
passenger.setOldPosAndRot();
|
|
++passenger.tickCount;
|
|
- ProfilerFiller gameprofilerfiller = this.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur
|
|
|
|
- gameprofilerfiller.push(() -> {
|
|
+ /*gameprofilerfiller.push(() -> { // Purpur
|
|
return BuiltInRegistries.ENTITY_TYPE.getKey(passenger.getType()).toString();
|
|
- });
|
|
- gameprofilerfiller.incrementCounter("tickPassenger");
|
|
+ });*/ // Purpur
|
|
+ //gameprofilerfiller.incrementCounter("tickPassenger"); // Purpur
|
|
// Paper start - EAR 2
|
|
if (isActive) {
|
|
passenger.rideTick();
|
|
@@ -1272,7 +1339,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
vehicle.positionRider(passenger);
|
|
}
|
|
// Paper end - EAR 2
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
Iterator iterator = passenger.getPassengers().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -1281,7 +1348,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
this.tickPassenger(passenger, entity2);
|
|
}
|
|
|
|
- } finally { timer.stopTiming(); }// Paper - EAR2 timings
|
|
+ //} finally { timer.stopTiming(); }// Paper - EAR2 timings // Purpur
|
|
}
|
|
} else {
|
|
passenger.stopRiding();
|
|
@@ -1301,14 +1368,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
|
|
}
|
|
|
|
- try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) {
|
|
+ //try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) { // Purpur
|
|
if (doFull) {
|
|
this.saveLevelData();
|
|
}
|
|
|
|
- this.timings.worldSaveChunks.startTiming(); // Paper
|
|
+ //this.timings.worldSaveChunks.startTiming(); // Paper // Purpur
|
|
if (!this.noSave()) chunkproviderserver.saveIncrementally();
|
|
- this.timings.worldSaveChunks.stopTiming(); // Paper
|
|
+ //this.timings.worldSaveChunks.stopTiming(); // Paper // Purpur
|
|
|
|
// Copied from save()
|
|
// CraftBukkit start - moved from MinecraftServer.saveChunks
|
|
@@ -1320,7 +1387,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
|
|
}
|
|
// CraftBukkit end
|
|
- }
|
|
+ //} // Purpur
|
|
}
|
|
// Paper end
|
|
|
|
@@ -1334,7 +1401,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
if (!savingDisabled) {
|
|
org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit
|
|
- try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper
|
|
+ //try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper // Purpur // Purpur
|
|
if (progressListener != null) {
|
|
progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel"));
|
|
}
|
|
@@ -1344,11 +1411,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
progressListener.progressStage(Component.translatable("menu.savingChunks"));
|
|
}
|
|
|
|
- timings.worldSaveChunks.startTiming(); // Paper
|
|
+ //timings.worldSaveChunks.startTiming(); // Paper // Purpur
|
|
if (!close) chunkproviderserver.save(flush); // Paper - rewrite chunk system
|
|
if (close) chunkproviderserver.close(true); // Paper - rewrite chunk system
|
|
- timings.worldSaveChunks.stopTiming(); // Paper
|
|
- }// Paper
|
|
+ //timings.worldSaveChunks.stopTiming(); // Paper // Purpur
|
|
+ //}// Paper // Purpur
|
|
// Paper - rewrite chunk system - entity saving moved into ChunkHolder
|
|
|
|
} else if (close) { chunkproviderserver.close(false); } // Paper - rewrite chunk system
|
|
@@ -2619,7 +2686,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
// Spigot Start
|
|
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
|
|
// Paper start
|
|
- if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) {
|
|
+ if (!entity.level.purpurConfig.playerVoidTrading && entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { // Purpur
|
|
merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED);
|
|
}
|
|
// Paper end
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
index 98df2463bf41fc736aa6a2b6ddf89e5abde6eb39..852266234cf3d63e3b23a71639e40defca91c1b8 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -277,6 +277,11 @@ public class ServerPlayer extends Player {
|
|
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
|
|
public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper
|
|
public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event
|
|
+ public boolean purpurClient = false; // Purpur
|
|
+ public boolean acceptingResourcePack = false; // Purpur
|
|
+ private boolean ramBar = false; // Purpur
|
|
+ private boolean tpsBar = false; // Purpur
|
|
+ private boolean compassBar = false; // Purpur
|
|
|
|
public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) {
|
|
super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile);
|
|
@@ -376,6 +381,7 @@ public class ServerPlayer extends Player {
|
|
this.bukkitPickUpLoot = true;
|
|
this.maxHealthCache = this.getMaxHealth();
|
|
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
|
|
+ this.spawnInvulnerableTime = world.purpurConfig.playerSpawnInvulnerableTicks; // Purpur
|
|
}
|
|
|
|
// Yes, this doesn't match Vanilla, but it's the best we can do for now.
|
|
@@ -515,6 +521,9 @@ public class ServerPlayer extends Player {
|
|
}
|
|
}
|
|
|
|
+ if (nbt.contains("Purpur.RamBar")) { this.ramBar = nbt.getBoolean("Purpur.RamBar"); } // Purpur
|
|
+ if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur
|
|
+ if (nbt.contains("Purpur.CompassBar")) { this.compassBar = nbt.getBoolean("Purpur.CompassBar"); } // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -581,6 +590,9 @@ public class ServerPlayer extends Player {
|
|
}
|
|
this.getBukkitEntity().setExtraData(nbt); // CraftBukkit
|
|
|
|
+ nbt.putBoolean("Purpur.RamBar", this.ramBar); // Purpur
|
|
+ nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur
|
|
+ nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur
|
|
}
|
|
|
|
// CraftBukkit start - World fallback code, either respawn location or global spawn
|
|
@@ -709,6 +721,15 @@ public class ServerPlayer extends Player {
|
|
this.trackStartFallingPosition();
|
|
this.trackEnteredOrExitedLavaOnVehicle();
|
|
this.advancements.flushDirty(this);
|
|
+
|
|
+ // Purpur start
|
|
+ if (this.level.purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && this.level.getGameTime() % 100 == 0) { // 5 seconds
|
|
+ MobEffectInstance nightVision = this.getEffect(MobEffects.NIGHT_VISION);
|
|
+ if (nightVision == null || nightVision.getDuration() <= 300) { // 15 seconds
|
|
+ this.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 400, 0)); // 20 seconds
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
public void doTick() {
|
|
@@ -947,6 +968,7 @@ public class ServerPlayer extends Player {
|
|
}));
|
|
Team scoreboardteambase = this.getTeam();
|
|
|
|
+ if (org.purpurmc.purpur.PurpurConfig.deathMessageOnlyBroadcastToAffectedPlayer) this.sendSystemMessage(ichatbasecomponent); else // Purpur
|
|
if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != Team.Visibility.ALWAYS) {
|
|
if (scoreboardteambase.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) {
|
|
this.server.getPlayerList().broadcastSystemToTeam(this, ichatbasecomponent);
|
|
@@ -1048,14 +1070,30 @@ public class ServerPlayer extends Player {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public boolean isSpawnInvulnerable() {
|
|
+ return spawnInvulnerableTime > 0 || frozen;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean hurt(DamageSource source, float amount) {
|
|
if (this.isInvulnerableTo(source)) {
|
|
return false;
|
|
} else {
|
|
+ // Purpur start
|
|
+ if (source.is(DamageTypeTags.IS_FALL)) { // Purpur
|
|
+ if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level.purpurConfig.minecartControllable && !level.purpurConfig.minecartControllableFallDamage) {
|
|
+ return false;
|
|
+ }
|
|
+ if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.Boat && !level.purpurConfig.boatsDoFallDamage) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && source.is(DamageTypeTags.IS_FALL);
|
|
|
|
- if (!flag && this.spawnInvulnerableTime > 0 && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) {
|
|
+ if (!flag && isSpawnInvulnerable() && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { // Purpur
|
|
return false;
|
|
} else {
|
|
Entity entity = source.getEntity();
|
|
@@ -1164,7 +1202,7 @@ public class ServerPlayer extends Player {
|
|
PortalInfo shapedetectorshape = this.findDimensionEntryPoint(worldserver);
|
|
|
|
if (shapedetectorshape != null) {
|
|
- worldserver1.getProfiler().push("moving");
|
|
+ //worldserver1.getProfiler().push("moving"); // Purpur
|
|
worldserver = shapedetectorshape.world; // CraftBukkit
|
|
if (worldserver == null) { } else // CraftBukkit - empty to fall through to null to event
|
|
if (resourcekey == LevelStem.OVERWORLD && worldserver.getTypeKey() == LevelStem.NETHER) { // CraftBukkit
|
|
@@ -1187,8 +1225,8 @@ public class ServerPlayer extends Player {
|
|
worldserver = ((CraftWorld) exit.getWorld()).getHandle();
|
|
// CraftBukkit end
|
|
|
|
- worldserver1.getProfiler().pop();
|
|
- worldserver1.getProfiler().push("placing");
|
|
+ //worldserver1.getProfiler().pop(); // Purpur
|
|
+ //worldserver1.getProfiler().push("placing"); // Purpur
|
|
if (true) { // CraftBukkit
|
|
this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds
|
|
|
|
@@ -1199,6 +1237,7 @@ public class ServerPlayer extends Player {
|
|
playerlist.sendPlayerPermissionLevel(this);
|
|
worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION);
|
|
this.unsetRemoved();
|
|
+ this.portalPos = io.papermc.paper.util.MCUtil.toBlockPosition(exit); // Purpur
|
|
|
|
// CraftBukkit end
|
|
this.setLevel(worldserver);
|
|
@@ -1206,7 +1245,7 @@ public class ServerPlayer extends Player {
|
|
this.connection.teleport(exit); // CraftBukkit - use internal teleport without event
|
|
this.connection.resetPosition();
|
|
worldserver.addDuringPortalTeleport(this);
|
|
- worldserver1.getProfiler().pop();
|
|
+ //worldserver1.getProfiler().pop(); // Purpur
|
|
this.triggerDimensionChangeTriggers(worldserver1);
|
|
this.connection.send(new ClientboundPlayerAbilitiesPacket(this.getAbilities()));
|
|
playerlist.sendLevelInfo(this, worldserver);
|
|
@@ -1235,6 +1274,7 @@ public class ServerPlayer extends Player {
|
|
}
|
|
// Paper end
|
|
|
|
+ this.spawnInvulnerableTime = worldserver.purpurConfig.playerSpawnInvulnerableTicks; // Purpur
|
|
return this;
|
|
}
|
|
}
|
|
@@ -1356,7 +1396,7 @@ public class ServerPlayer extends Player {
|
|
return entitymonster.isPreventingPlayerRest(this);
|
|
});
|
|
|
|
- if (!list.isEmpty()) {
|
|
+ if (!this.level.purpurConfig.playerSleepNearMonsters && !list.isEmpty()) { // Purpur
|
|
return Either.left(Player.BedSleepingProblem.NOT_SAFE);
|
|
}
|
|
}
|
|
@@ -1492,6 +1532,7 @@ public class ServerPlayer extends Player {
|
|
|
|
@Override
|
|
public void openTextEdit(SignBlockEntity sign) {
|
|
+ if (level.purpurConfig.signAllowColors) this.connection.send(sign.getTranslatedUpdatePacket(textFilteringEnabled)); // Purpur
|
|
sign.setAllowedPlayerEditor(this.getUUID());
|
|
this.connection.send(new ClientboundBlockUpdatePacket(this.level, sign.getBlockPos()));
|
|
this.connection.send(new ClientboundOpenSignEditorPacket(sign.getBlockPos()));
|
|
@@ -1728,6 +1769,26 @@ public class ServerPlayer extends Player {
|
|
this.lastSentExp = -1; // CraftBukkit - Added to reset
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void sendActionBarMessage(@Nullable String message) {
|
|
+ if (message != null && !message.isEmpty()) {
|
|
+ sendActionBarMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void sendActionBarMessage(@Nullable net.kyori.adventure.text.Component message) {
|
|
+ if (message != null) {
|
|
+ sendActionBarMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void sendActionBarMessage(@Nullable Component message) {
|
|
+ if (message != null) {
|
|
+ displayClientMessage(message, true);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void displayClientMessage(Component message, boolean overlay) {
|
|
this.sendSystemMessage(message, overlay);
|
|
@@ -2027,6 +2088,7 @@ public class ServerPlayer extends Player {
|
|
}
|
|
|
|
public void sendTexturePack(String url, String hash, boolean required, @Nullable Component resourcePackPrompt) {
|
|
+ this.acceptingResourcePack = true; // Purpur
|
|
this.connection.send(new ClientboundResourcePackPacket(url, hash, required, resourcePackPrompt));
|
|
}
|
|
|
|
@@ -2041,8 +2103,68 @@ public class ServerPlayer extends Player {
|
|
|
|
public void resetLastActionTime() {
|
|
this.lastActionTime = Util.getMillis();
|
|
+ this.setAfk(false); // Purpur
|
|
+ }
|
|
+
|
|
+ // Purpur Start
|
|
+ private boolean isAfk = false;
|
|
+
|
|
+ @Override
|
|
+ public void setAfk(boolean afk) {
|
|
+ if (this.isAfk == afk) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ String msg = afk ? org.purpurmc.purpur.PurpurConfig.afkBroadcastAway : org.purpurmc.purpur.PurpurConfig.afkBroadcastBack;
|
|
+
|
|
+ org.purpurmc.purpur.event.PlayerAFKEvent event = new org.purpurmc.purpur.event.PlayerAFKEvent(this.getBukkitEntity(), afk, this.level.purpurConfig.idleTimeoutKick, msg, !Bukkit.isPrimaryThread());
|
|
+ if (!event.callEvent() || event.shouldKick()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ this.isAfk = afk;
|
|
+
|
|
+ if (!afk) {
|
|
+ resetLastActionTime();
|
|
+ }
|
|
+
|
|
+ msg = event.getBroadcastMsg();
|
|
+ if (msg != null && !msg.isEmpty()) {
|
|
+ String playerName = this.getGameProfile().getName();
|
|
+ if (org.purpurmc.purpur.PurpurConfig.afkBroadcastUseDisplayName) {
|
|
+ net.kyori.adventure.text.Component playerDisplayNameComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(this.getBukkitEntity().getDisplayName());
|
|
+ playerName = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(playerDisplayNameComponent);
|
|
+ }
|
|
+ server.getPlayerList().broadcastMiniMessage(String.format(msg, playerName), false);
|
|
+ }
|
|
+
|
|
+ if (this.level.purpurConfig.idleTimeoutUpdateTabList) {
|
|
+ String scoreboardName = getScoreboardName();
|
|
+ String playerListName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().serialize(getBukkitEntity().playerListName());
|
|
+ String[] split = playerListName.split(scoreboardName);
|
|
+ String prefix = (split.length > 0 ? split[0] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix, "");
|
|
+ String suffix = (split.length > 1 ? split[1] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, "");
|
|
+ if (afk) {
|
|
+ getBukkitEntity().setPlayerListName(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix + prefix + scoreboardName + suffix + org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, true);
|
|
+ } else {
|
|
+ getBukkitEntity().setPlayerListName(prefix + scoreboardName + suffix);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ((ServerLevel) this.level).updateSleepingPlayerList();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isAfk() {
|
|
+ return this.isAfk;
|
|
}
|
|
|
|
+ @Override
|
|
+ public boolean canBeCollidedWith() {
|
|
+ return !this.isAfk() && super.canBeCollidedWith();
|
|
+ }
|
|
+ // Purpur End
|
|
+
|
|
public ServerStatsCounter getStats() {
|
|
return this.stats;
|
|
}
|
|
@@ -2514,8 +2636,16 @@ public class ServerPlayer extends Player {
|
|
|
|
@Override
|
|
public boolean isImmobile() {
|
|
- return super.isImmobile() || (this.connection != null && this.connection.isDisconnected()); // Paper
|
|
+ return super.isImmobile() || frozen || (this.connection != null && this.connection.isDisconnected()); // Paper // Purpur
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ private boolean frozen = false;
|
|
+
|
|
+ public void setFrozen(boolean frozen) {
|
|
+ this.frozen = frozen;
|
|
}
|
|
+ // Purpur end
|
|
|
|
@Override
|
|
public Scoreboard getScoreboard() {
|
|
@@ -2564,4 +2694,50 @@ public class ServerPlayer extends Player {
|
|
return (CraftPlayer) super.getBukkitEntity();
|
|
}
|
|
// CraftBukkit end
|
|
+
|
|
+ // Purpur start
|
|
+ public void teleport(Location to) {
|
|
+ this.ejectPassengers();
|
|
+ this.stopRiding(true);
|
|
+
|
|
+ if (this.isSleeping()) {
|
|
+ this.stopSleepInBed(true, false);
|
|
+ }
|
|
+
|
|
+ if (this.containerMenu != this.inventoryMenu) {
|
|
+ this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.TELEPORT);
|
|
+ }
|
|
+
|
|
+ ServerLevel toLevel = ((CraftWorld) to.getWorld()).getHandle();
|
|
+ if (this.level == toLevel) {
|
|
+ this.connection.internalTeleport(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch(), java.util.EnumSet.noneOf(net.minecraft.world.entity.RelativeMovement.class));
|
|
+ } else {
|
|
+ this.server.getPlayerList().respawn(this, toLevel, true, to, !toLevel.paperConfig().environment.disableTeleportationSuffocationCheck, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.DEATH);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean ramBar() {
|
|
+ return this.ramBar;
|
|
+ }
|
|
+
|
|
+ public void ramBar(boolean ramBar) {
|
|
+ this.ramBar = ramBar;
|
|
+ }
|
|
+
|
|
+ public boolean tpsBar() {
|
|
+ return this.tpsBar;
|
|
+ }
|
|
+
|
|
+ public void tpsBar(boolean tpsBar) {
|
|
+ this.tpsBar = tpsBar;
|
|
+ }
|
|
+
|
|
+ public boolean compassBar() {
|
|
+ return this.compassBar;
|
|
+ }
|
|
+
|
|
+ public void compassBar(boolean compassBar) {
|
|
+ this.compassBar = compassBar;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
index 1d33c02088c150189d7f4b0aa27f6a1de96b11cf..75f29f6dddf50ccf7ef43ecfa602ccade3c9004d 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
|
@@ -397,6 +397,7 @@ public class ServerPlayerGameMode {
|
|
} else {capturedBlockEntity = true;} // Paper end
|
|
return false;
|
|
}
|
|
+ if (this.player.level.purpurConfig.slabHalfBreak && this.player.isShiftKeyDown() && iblockdata.getBlock() instanceof net.minecraft.world.level.block.SlabBlock && ((net.minecraft.world.level.block.SlabBlock) iblockdata.getBlock()).halfBreak(iblockdata, pos, this.player)) return true; // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
|
|
@@ -427,7 +428,7 @@ public class ServerPlayerGameMode {
|
|
|
|
ItemStack mainHandStack = null; // Paper
|
|
boolean isCorrectTool = false; // Paper
|
|
- if (this.isCreative()) {
|
|
+ if (this.isCreative() || (this.level.purpurConfig.shulkerBoxAllowOversizedStacks && block instanceof net.minecraft.world.level.block.ShulkerBoxBlock)) { // Purpur
|
|
// return true; // CraftBukkit
|
|
} else {
|
|
ItemStack itemstack = this.player.getMainHandItem();
|
|
@@ -516,6 +517,7 @@ public class ServerPlayerGameMode {
|
|
public InteractionHand interactHand;
|
|
public ItemStack interactItemStack;
|
|
public InteractionResult useItemOn(ServerPlayer player, Level world, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) {
|
|
+ if (shiftClickMended(stack)) return InteractionResult.SUCCESS; // Purpur
|
|
BlockPos blockposition = hitResult.getBlockPos();
|
|
BlockState iblockdata = world.getBlockState(blockposition);
|
|
InteractionResult enuminteractionresult = InteractionResult.PASS;
|
|
@@ -576,7 +578,7 @@ public class ServerPlayerGameMode {
|
|
boolean flag1 = player.isSecondaryUseActive() && flag;
|
|
ItemStack itemstack1 = stack.copy();
|
|
|
|
- if (!flag1) {
|
|
+ if (!flag1 || (player.level.purpurConfig.composterBulkProcess && iblockdata.is(Blocks.COMPOSTER))) { // Purpur
|
|
enuminteractionresult = iblockdata.use(world, player, hand, hitResult);
|
|
|
|
if (enuminteractionresult.consumesAction()) {
|
|
@@ -612,4 +614,18 @@ public class ServerPlayerGameMode {
|
|
public void setLevel(ServerLevel world) {
|
|
this.level = world;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public boolean shiftClickMended(ItemStack itemstack) {
|
|
+ if (this.player.level.purpurConfig.shiftRightClickRepairsMendingPoints > 0 && this.player.isShiftKeyDown() && this.player.getBukkitEntity().hasPermission("purpur.mending_shift_click")) {
|
|
+ int points = Math.min(this.player.totalExperience, this.player.level.purpurConfig.shiftRightClickRepairsMendingPoints);
|
|
+ if (points > 0 && itemstack.isDamaged() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MENDING, itemstack) > 0) {
|
|
+ this.player.giveExperiencePoints(-points);
|
|
+ this.player.level.addFreshEntity(new net.minecraft.world.entity.ExperienceOrb(this.player.level, this.player.getX(), this.player.getY(), this.player.getZ(), points, org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN, this.player, this.player));
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java
|
|
index 877498729c66de9aa6a27c9148f7494d7895615c..acd7468ee3c86d3456e96e4ec3d7e6a4c612e89d 100644
|
|
--- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java
|
|
+++ b/src/main/java/net/minecraft/server/level/WorldGenRegion.java
|
|
@@ -297,6 +297,7 @@ public class WorldGenRegion implements WorldGenLevel {
|
|
return true;
|
|
} else {
|
|
// Paper start
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressSetBlockFarChunk) // Purpur
|
|
if (!hasSetFarWarned) {
|
|
Util.logAndPauseIfInIde("Detected setBlock in a far chunk [" + i + ", " + j + "], pos: " + pos + ", status: " + this.generatingStatus + (this.currentlyGenerating == null ? "" : ", currently generating: " + (String) this.currentlyGenerating.get()));
|
|
hasSetFarWarned = true;
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
index 9d2d72fe48b69be2f6ebe74309673a3a4e51eae4..af8cb1f1f0c128923495f51e4828003133ce766b 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
@@ -263,6 +263,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
private long keepAliveTime = Util.getMillis();
|
|
private boolean keepAlivePending;
|
|
private long keepAliveChallenge;
|
|
+ private it.unimi.dsi.fastutil.longs.LongList keepAlives = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Purpur
|
|
// CraftBukkit start - multithreaded fields
|
|
private final AtomicInteger chatSpamTickCount = new AtomicInteger();
|
|
private final java.util.concurrent.atomic.AtomicInteger tabSpamLimiter = new java.util.concurrent.atomic.AtomicInteger(); // Paper - configurable tab spam limits
|
|
@@ -340,6 +341,20 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
private boolean justTeleported = false;
|
|
private boolean hasMoved; // Spigot
|
|
|
|
+ // Purpur start
|
|
+ private final com.google.common.cache.LoadingCache<CraftPlayer, Boolean> kickPermissionCache = com.google.common.cache.CacheBuilder.newBuilder()
|
|
+ .maximumSize(1000)
|
|
+ .expireAfterWrite(1, java.util.concurrent.TimeUnit.MINUTES)
|
|
+ .build(
|
|
+ new com.google.common.cache.CacheLoader<>() {
|
|
+ @Override
|
|
+ public Boolean load(CraftPlayer player) {
|
|
+ return player.hasPermission("purpur.bypassIdleKick");
|
|
+ }
|
|
+ }
|
|
+ );
|
|
+ // Purpur end
|
|
+
|
|
public CraftPlayer getCraftPlayer() {
|
|
return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity();
|
|
}
|
|
@@ -395,12 +410,27 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
this.aboveGroundVehicleTickCount = 0;
|
|
}
|
|
|
|
- this.server.getProfiler().push("keepAlive");
|
|
+ //this.server.getProfiler().push("keepAlive"); // Purpur
|
|
// Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings
|
|
// This should effectively place the keepalive handling back to "as it was" before 1.12.2
|
|
long currentTime = Util.getMillis();
|
|
long elapsedTime = currentTime - this.keepAliveTime;
|
|
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) {
|
|
+ if (elapsedTime >= 1000L) { // 1 second
|
|
+ if (!processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) {
|
|
+ LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName());
|
|
+ disconnect(Component.translatable("disconnect.timeout"), org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT);
|
|
+ } else {
|
|
+ keepAliveTime = currentTime; // hijack this field for 1 second intervals
|
|
+ keepAlives.add(currentTime); // currentTime is ID
|
|
+ send(new ClientboundKeepAlivePacket(currentTime));
|
|
+ }
|
|
+ }
|
|
+ } else
|
|
+ // Purpur end
|
|
+
|
|
if (this.keepAlivePending) {
|
|
if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
|
|
ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info
|
|
@@ -416,7 +446,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
}
|
|
// Paper end
|
|
|
|
- this.server.getProfiler().pop();
|
|
+ //this.server.getProfiler().pop(); // Purpur
|
|
// CraftBukkit start
|
|
for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !this.chatSpamTickCount.compareAndSet(spam, spam - 1); ) ;
|
|
if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable
|
|
@@ -433,6 +463,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
}
|
|
|
|
if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) (this.server.getPlayerIdleTimeout() * 1000 * 60) && !this.player.wonGame) { // Paper - Prevent AFK kick while watching end credits.
|
|
+ // Purpur start
|
|
+ this.player.setAfk(true);
|
|
+ if (!this.player.level.purpurConfig.idleTimeoutKick || (!Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK")) && kickPermissionCache.getUnchecked(this.player.getBukkitEntity()))) {
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
|
|
this.disconnect(Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause
|
|
}
|
|
@@ -744,6 +780,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
this.lastYaw = to.getYaw();
|
|
this.lastPitch = to.getPitch();
|
|
|
|
+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur
|
|
+
|
|
// Skip the first time we do this
|
|
if (true) { // Spigot - don't skip any move events
|
|
Location oldTo = to.clone();
|
|
@@ -820,6 +858,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
if (packet.getId() == this.awaitingTeleport) {
|
|
if (this.awaitingPositionFromClient == null) {
|
|
this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause
|
|
+ ServerGamePacketListenerImpl.LOGGER.warn("Disconnected on accept teleport packet. Was not expecting position data from client at this time"); // Purpur
|
|
return;
|
|
}
|
|
|
|
@@ -1224,10 +1263,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
int maxBookPageSize = io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.pageMax;
|
|
double multiplier = Math.max(0.3D, Math.min(1D, io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.totalMultiplier));
|
|
long byteAllowed = maxBookPageSize;
|
|
+ ItemStack itemstack = this.player.getInventory().getItem(packet.getSlot()); // Purpur
|
|
for (String testString : pageList) {
|
|
int byteLength = testString.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
|
|
if (byteLength > 256 * 4) {
|
|
ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send a book with with a page too large!");
|
|
+ org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent event = new org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent(player.getBukkitEntity(), itemstack.asBukkitCopy()); if (event.shouldKickPlayer()) // Purpur
|
|
server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause
|
|
return;
|
|
}
|
|
@@ -1251,6 +1292,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
|
|
if (byteTotal > byteAllowed) {
|
|
ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send too large of a book. Book Size: " + byteTotal + " - Allowed: "+ byteAllowed + " - Pages: " + pageList.size());
|
|
+ org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent event = new org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent(player.getBukkitEntity(), itemstack.asBukkitCopy()); if (event.shouldKickPlayer()) // Purpur
|
|
server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause
|
|
return;
|
|
}
|
|
@@ -1304,13 +1346,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
itemstack1.setTag(nbttagcompound.copy());
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ boolean hasPerm = getCraftPlayer().hasPermission("purpur.book.color.edit") || getCraftPlayer().hasPermission("purpur.book.color.sign");
|
|
itemstack1.addTagElement("author", StringTag.valueOf(this.player.getName().getString()));
|
|
if (this.player.isTextFilteringEnabled()) {
|
|
- itemstack1.addTagElement("title", StringTag.valueOf(title.filteredOrEmpty()));
|
|
+ itemstack1.addTagElement("title", StringTag.valueOf(color(title.filteredOrEmpty(), hasPerm)));
|
|
} else {
|
|
- itemstack1.addTagElement("filtered_title", StringTag.valueOf(title.filteredOrEmpty()));
|
|
- itemstack1.addTagElement("title", StringTag.valueOf(title.raw()));
|
|
+ itemstack1.addTagElement("filtered_title", StringTag.valueOf(color(title.filteredOrEmpty(), hasPerm)));
|
|
+ itemstack1.addTagElement("title", StringTag.valueOf(color(title.raw(), hasPerm)));
|
|
}
|
|
+ // Purpur end
|
|
|
|
this.updateBookPages(pages, (s) -> {
|
|
return Component.Serializer.toJson(Component.literal(s));
|
|
@@ -1322,10 +1367,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
private void updateBookPages(List<FilteredText> list, UnaryOperator<String> unaryoperator, ItemStack itemstack, int slot, ItemStack handItem) { // CraftBukkit
|
|
ListTag nbttaglist = new ListTag();
|
|
|
|
+ // Purpur start
|
|
+ boolean hasPerm = getCraftPlayer().hasPermission("purpur.book.color.edit");
|
|
if (this.player.isTextFilteringEnabled()) {
|
|
- Stream<StringTag> stream = list.stream().map((filteredtext) -> { // CraftBukkit - decompile error
|
|
- return StringTag.valueOf((String) unaryoperator.apply(filteredtext.filteredOrEmpty()));
|
|
+ Stream<StringTag> stream = list.stream().map(s -> color(s.filteredOrEmpty(), hasPerm, false)).map((s) -> { // CraftBukkit - decompile error
|
|
+ return StringTag.valueOf((String) unaryoperator.apply(s));
|
|
});
|
|
+ // Purpur end
|
|
|
|
Objects.requireNonNull(nbttaglist);
|
|
stream.forEach(nbttaglist::add);
|
|
@@ -1335,11 +1383,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
|
|
for (int j = list.size(); i < j; ++i) {
|
|
FilteredText filteredtext = (FilteredText) list.get(i);
|
|
- String s = filteredtext.raw();
|
|
+ String s = color(filteredtext.raw(), hasPerm, false); // Purpur
|
|
|
|
nbttaglist.add(StringTag.valueOf((String) unaryoperator.apply(s)));
|
|
if (filteredtext.isFiltered()) {
|
|
- nbttagcompound.putString(String.valueOf(i), (String) unaryoperator.apply(filteredtext.filteredOrEmpty()));
|
|
+ nbttagcompound.putString(String.valueOf(i), (String) unaryoperator.apply((String) color(filteredtext.filteredOrEmpty(), hasPerm, false))); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -1352,6 +1400,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
this.player.getInventory().setItem(slot, CraftEventFactory.handleEditBookEvent(player, slot, handItem, itemstack)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent)
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private String color(String str, boolean hasPerm) {
|
|
+ return color(str, hasPerm, true);
|
|
+ }
|
|
+
|
|
+ private String color(String str, boolean hasPerm, boolean parseHex) {
|
|
+ return hasPerm ? org.bukkit.ChatColor.color(str, parseHex) : str;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void handleEntityTagQuery(ServerboundEntityTagQuery packet) {
|
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
|
@@ -1381,8 +1439,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
@Override
|
|
public void handleMovePlayer(ServerboundMovePlayerPacket packet) {
|
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
|
- if (ServerGamePacketListenerImpl.containsInvalidValues(packet.getX(0.0D), packet.getY(0.0D), packet.getZ(0.0D), packet.getYRot(0.0F), packet.getXRot(0.0F))) {
|
|
+ // Purpur start
|
|
+ boolean invalidX = Double.isNaN(packet.getX(0.0D));
|
|
+ boolean invalidY = Double.isNaN(packet.getY(0.0D));
|
|
+ boolean invalidZ = Double.isNaN(packet.getZ(0.0D));
|
|
+ boolean invalidYaw = !Floats.isFinite(packet.getYRot(0.0F));
|
|
+ boolean invalidPitch = !Floats.isFinite(packet.getXRot(0.0F));
|
|
+ if (invalidX || invalidY || invalidZ || invalidYaw || invalidPitch) {
|
|
this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause
|
|
+ ServerGamePacketListenerImpl.LOGGER.warn(String.format("Disconnected on move player packet. Invalid data: x=%b, y=%b, z=%b, yaw=%b, pitch=%b", invalidX, invalidY, invalidZ, invalidYaw, invalidPitch));
|
|
+ // Purpur end
|
|
} else {
|
|
ServerLevel worldserver = this.player.getLevel();
|
|
|
|
@@ -1548,7 +1614,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
|
|
if (!this.player.isChangingDimension() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot
|
|
flag2 = true; // Paper - diff on change, this should be moved wrongly
|
|
- ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
|
|
+ ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), d11); // Purpur
|
|
}
|
|
|
|
this.player.absMoveTo(d0, d1, d2, f, f1);
|
|
@@ -1599,6 +1665,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
this.lastYaw = to.getYaw();
|
|
this.lastPitch = to.getPitch();
|
|
|
|
+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur
|
|
+
|
|
// Skip the first time we do this
|
|
if (from.getX() != Double.MAX_VALUE) {
|
|
Location oldTo = to.clone();
|
|
@@ -1638,6 +1706,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
this.player.resetFallDistance();
|
|
}
|
|
|
|
+ // Purpur Start
|
|
+ if (this.player.level.purpurConfig.dontRunWithScissors && this.player.isSprinting() && !(this.player.level.purpurConfig.ignoreScissorsInWater && this.player.isInWater()) && !(this.player.level.purpurConfig.ignoreScissorsInLava && this.player.isInLava()) && (isScissor(this.player.getItemInHand(InteractionHand.MAIN_HAND)) || isScissor(this.player.getItemInHand(InteractionHand.OFF_HAND))) && (int) (Math.random() * 10) == 0) {
|
|
+ this.player.hurt(this.player.damageSources().magic(), (float) this.player.level.purpurConfig.scissorsRunningDamage);
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.dontRunWithScissors.isBlank()) this.player.sendActionBarMessage(org.purpurmc.purpur.PurpurConfig.dontRunWithScissors);
|
|
+ }
|
|
+ // Purpur End
|
|
+
|
|
this.player.checkMovementStatistics(this.player.getX() - d3, this.player.getY() - d4, this.player.getZ() - d5);
|
|
this.lastGoodX = this.player.getX();
|
|
this.lastGoodY = this.player.getY();
|
|
@@ -1671,6 +1746,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
}
|
|
// Paper end - optimise out extra getCubes
|
|
|
|
+ // Purpur start
|
|
+ public boolean isScissor(ItemStack stack) {
|
|
+ return stack.is(Items.SHEARS) && (stack.getTag() == null || stack.getTag().getInt("CustomModelData") == 0);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box) {
|
|
Iterable<VoxelShape> iterable = world.getCollisions(this.player, this.player.getBoundingBox().deflate(9.999999747378752E-6D));
|
|
VoxelShape voxelshape = Shapes.create(box.deflate(9.999999747378752E-6D));
|
|
@@ -2015,6 +2096,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
|
|
boolean cancelled;
|
|
if (movingobjectposition == null || movingobjectposition.getType() != HitResult.Type.BLOCK) {
|
|
+ if (this.player.gameMode.shiftClickMended(itemstack)) return; // Purpur
|
|
org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack, enumhand);
|
|
cancelled = event.useItemInHand() == Event.Result.DENY;
|
|
} else {
|
|
@@ -2068,12 +2150,21 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
@Override
|
|
public void handleResourcePackResponse(ServerboundResourcePackPacket packet) {
|
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
|
+ // Purpur start
|
|
+ if (player.level.purpurConfig.playerInvulnerableWhileAcceptingResourcePack && !this.player.acceptingResourcePack) {
|
|
+ ServerGamePacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack packet exploitation attempt", this.player.getName());
|
|
+ this.disconnect(Component.translatable("multiplayer.texturePrompt.failure.line1"), org.bukkit.event.player.PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // "Server resource pack couldn't be applied"
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
if (packet.getAction() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) {
|
|
ServerGamePacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack rejection", this.player.getGameProfile().getName()); // Paper - Don't print component in resource pack rejection message
|
|
this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), org.bukkit.event.player.PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - add cause
|
|
}
|
|
// Paper start
|
|
PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packet.action.ordinal()];
|
|
+ if (player.level.purpurConfig.playerInvulnerableWhileAcceptingResourcePack) player.setFrozen(packStatus == PlayerResourcePackStatusEvent.Status.ACCEPTED); // Purpur
|
|
+ this.player.acceptingResourcePack = packStatus == PlayerResourcePackStatusEvent.Status.ACCEPTED; // Purpur
|
|
player.getBukkitEntity().setResourcePackStatus(packStatus);
|
|
this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(this.getCraftPlayer(), packStatus)); // CraftBukkit
|
|
// Paper end
|
|
@@ -2370,7 +2461,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
do {
|
|
instant1 = (Instant) this.lastChatTimeStamp.get();
|
|
if (timestamp.isBefore(instant1)) {
|
|
- return false;
|
|
+ return !org.purpurmc.purpur.PurpurConfig.kickForOutOfOrderChat; // Purpur
|
|
}
|
|
} while (!this.lastChatTimeStamp.compareAndSet(instant1, timestamp));
|
|
|
|
@@ -2507,7 +2598,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
}
|
|
}
|
|
// Paper End
|
|
- co.aikar.timings.MinecraftTimings.playerCommandTimer.startTiming(); // Paper
|
|
+ //co.aikar.timings.MinecraftTimings.playerCommandTimer.startTiming(); // Paper // Purpur
|
|
if ( org.spigotmc.SpigotConfig.logCommands ) // Spigot
|
|
this.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + s);
|
|
|
|
@@ -2517,7 +2608,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
this.cserver.getPluginManager().callEvent(event);
|
|
|
|
if (event.isCancelled()) {
|
|
- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper
|
|
+ //co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper // Purpur
|
|
return;
|
|
}
|
|
|
|
@@ -2530,7 +2621,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
java.util.logging.Logger.getLogger(ServerGamePacketListenerImpl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
|
|
return;
|
|
} finally {
|
|
- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper
|
|
+ //co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper // Purpur
|
|
}
|
|
}
|
|
// CraftBukkit end
|
|
@@ -2796,6 +2887,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
AABB axisalignedbb = entity.getBoundingBox();
|
|
|
|
if (axisalignedbb.distanceToSqr(this.player.getEyePosition()) < ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) {
|
|
+ if (entity instanceof Mob mob) mob.ticksSinceLastInteraction = 0; // Purpur
|
|
packet.dispatch(new ServerboundInteractPacket.Handler() {
|
|
private void performInteraction(InteractionHand enumhand, ServerGamePacketListenerImpl.EntityInteraction playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit
|
|
ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand);
|
|
@@ -2809,6 +2901,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
|
|
ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event);
|
|
|
|
+ player.processClick(enumhand); // Purpur
|
|
+
|
|
// Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
|
|
if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
|
|
entity.getEntityData().resendPossiblyDesyncedEntity(player); // Paper - The entire mob gets deleted, so resend it.
|
|
@@ -3360,6 +3454,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
}
|
|
}
|
|
}
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.fixNetworkSerializedItemsInCreative) {
|
|
+ var tag = itemstack.getTagElement("Purpur.OriginalItem");
|
|
+ if (tag != null) itemstack = ItemStack.of(tag);
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
boolean flag1 = packet.getSlotNum() >= 1 && packet.getSlotNum() <= 45;
|
|
boolean flag2 = itemstack.isEmpty() || itemstack.getDamageValue() >= 0 && itemstack.getCount() <= 64 && !itemstack.isEmpty();
|
|
@@ -3466,11 +3566,17 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
for (int i = 0; i < signText.size(); ++i) {
|
|
FilteredText filteredtext = (FilteredText) signText.get(i);
|
|
|
|
- if (this.player.isTextFilteringEnabled()) {
|
|
- lines.add(net.kyori.adventure.text.Component.text(SharedConstants.filterText(filteredtext.filteredOrEmpty()))); // Paper - adventure
|
|
+ // Purpur start
|
|
+ String line = SharedConstants.filterText(this.player.isTextFilteringEnabled() ? filteredtext.filteredOrEmpty() : filteredtext.raw());
|
|
+ if (worldserver.purpurConfig.signAllowColors) {
|
|
+ if (player.hasPermission("purpur.sign.color")) line = line.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1");
|
|
+ if (player.hasPermission("purpur.sign.style")) line = line.replaceAll("(?i)&([l-or])", "\u00a7$1");
|
|
+ if (player.hasPermission("purpur.sign.magic")) line = line.replaceAll("(?i)&([kr])", "\u00a7$1");
|
|
+ lines.add(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(line));
|
|
} else {
|
|
- lines.add(net.kyori.adventure.text.Component.text(SharedConstants.filterText(filteredtext.raw()))); // Paper - adventure
|
|
+ lines.add(net.kyori.adventure.text.Component.text(line));
|
|
}
|
|
+ // Purpur end
|
|
}
|
|
SignChangeEvent event = new SignChangeEvent((org.bukkit.craftbukkit.block.CraftBlock) player.getWorld().getBlockAt(x, y, z), this.player.getBukkitEntity(), lines);
|
|
this.cserver.getPluginManager().callEvent(event);
|
|
@@ -3492,6 +3598,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
|
|
@Override
|
|
public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) {
|
|
+ long id = packet.getId();
|
|
+ if (keepAlives.size() > 0 && keepAlives.contains(id)) {
|
|
+ int ping = (int) (Util.getMillis() - id);
|
|
+ player.latency = (player.latency * 3 + ping) / 4;
|
|
+ keepAlives.clear(); // we got a valid response, lets roll with it and forget the rest
|
|
+ }
|
|
+ } else
|
|
+ // Purpur end
|
|
//PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); // CraftBukkit // Paper - This shouldn't be on the main thread
|
|
if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) {
|
|
int i = (int) (Util.getMillis() - this.keepAliveTime);
|
|
@@ -3542,6 +3658,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
private static final ResourceLocation CUSTOM_UNREGISTER = new ResourceLocation("unregister");
|
|
|
|
private static final ResourceLocation MINECRAFT_BRAND = new ResourceLocation("brand"); // Paper - Brand support
|
|
+ private static final ResourceLocation PURPUR_CLIENT = new ResourceLocation("purpur", "client"); // Purpur
|
|
|
|
@Override
|
|
public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
|
|
@@ -3566,6 +3683,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex);
|
|
this.disconnect("Invalid payload UNREGISTER!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
|
|
}
|
|
+ // Purpur start
|
|
+ } else if (packet.identifier.equals(PURPUR_CLIENT)) {
|
|
+ try {
|
|
+ player.purpurClient = true;
|
|
+ } catch (Exception ignore) {
|
|
+ }
|
|
+ // Purpur end
|
|
} else {
|
|
try {
|
|
byte[] data = new byte[packet.data.readableBytes()];
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
index 2ff578e4a953ffcf5176815ba8e3f06f73499989..f719f8aafe7c75e2ef8fcb05f556a8d6bd94b9a0 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
@@ -220,6 +220,8 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
return false;
|
|
}
|
|
|
|
+ if (true) return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(in).matches(); // Purpur
|
|
+
|
|
for (int i = 0, len = in.length(); i < len; ++i) {
|
|
char c = in.charAt(i);
|
|
|
|
@@ -339,7 +341,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
ServerLoginPacketListenerImpl.this.gameProfile = gameprofile;
|
|
ServerLoginPacketListenerImpl.this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT;
|
|
} else {
|
|
- ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username"));
|
|
+ ServerLoginPacketListenerImpl.this.disconnect(org.purpurmc.purpur.PurpurConfig.unverifiedUsername.equals("default") ? Component.translatable("multiplayer.disconnect.unverified_username") : io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.unverifiedUsername))); // Purpur
|
|
ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", gameprofile.getName());
|
|
}
|
|
} catch (AuthenticationUnavailableException authenticationunavailableexception) {
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
|
|
index 2c13147bc063a09bb7907d6f90c3a1e811a09eb1..3edb0c392cec7fdabebad81da1a8b06a700fbfca 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
|
|
@@ -153,6 +153,7 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene
|
|
this.connection.send(new ClientboundStatusResponsePacket(ping));
|
|
// CraftBukkit end
|
|
*/
|
|
+ if (MinecraftServer.getServer().getStatus().version().isEmpty()) return; // Purpur - do not respond to pings before we know the protocol version
|
|
com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(MinecraftServer.getServer(), this.connection);
|
|
// Paper end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java b/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java
|
|
index 9ddbfcf80d9a381dace78a62880f85a4d767e0eb..7383c7d3820dce06108eaafd236a7c6c06a10a42 100644
|
|
--- a/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java
|
|
+++ b/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java
|
|
@@ -9,11 +9,11 @@ public interface ResourceManagerReloadListener extends PreparableReloadListener
|
|
@Override
|
|
default CompletableFuture<Void> reload(PreparableReloadListener.PreparationBarrier synchronizer, ResourceManager manager, ProfilerFiller prepareProfiler, ProfilerFiller applyProfiler, Executor prepareExecutor, Executor applyExecutor) {
|
|
return synchronizer.wait(Unit.INSTANCE).thenRunAsync(() -> {
|
|
- applyProfiler.startTick();
|
|
- applyProfiler.push("listener");
|
|
+ //applyProfiler.startTick(); // Purpur
|
|
+ //applyProfiler.push("listener"); // Purpur
|
|
this.onResourceManagerReload(manager);
|
|
- applyProfiler.pop();
|
|
- applyProfiler.endTick();
|
|
+ //applyProfiler.pop(); // Purpur
|
|
+ //applyProfiler.endTick(); // Purpur
|
|
}, applyExecutor);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
index 92e758a286a5db079c32d53cc52c8a422457daef..ff56981a03b55f9ee1ec8ad36adaf9849b2c914b 100644
|
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
@@ -469,6 +469,7 @@ public abstract class PlayerList {
|
|
scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam);
|
|
}
|
|
// Paper end
|
|
+ org.purpurmc.purpur.task.BossBarTask.addToAll(player); // Purpur
|
|
// CraftBukkit - Moved from above, added world
|
|
PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), s1, player.getId(), worldserver1.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ());
|
|
}
|
|
@@ -578,6 +579,8 @@ public abstract class PlayerList {
|
|
}
|
|
public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer, net.kyori.adventure.text.Component leaveMessage) {
|
|
// Paper end
|
|
+ org.purpurmc.purpur.task.BossBarTask.removeFromAll(entityplayer.getBukkitEntity()); // Purpur
|
|
+
|
|
ServerLevel worldserver = entityplayer.getLevel();
|
|
|
|
entityplayer.awardStat(Stats.LEAVE_GAME);
|
|
@@ -731,7 +734,7 @@ public abstract class PlayerList {
|
|
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, PaperAdventure.asAdventure(ichatmutablecomponent)); // Paper - Adventure
|
|
} else {
|
|
// return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile) ? IChatBaseComponent.translatable("multiplayer.disconnect.server_full") : null;
|
|
- if (this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile)) {
|
|
+ if (this.players.size() >= this.maxPlayers && !(player.hasPermission("purpur.joinfullserver") || this.canBypassPlayerLimit(gameprofile))) { // Purpur
|
|
event.disallow(PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure
|
|
}
|
|
}
|
|
@@ -969,6 +972,8 @@ public abstract class PlayerList {
|
|
}
|
|
// Paper end
|
|
|
|
+ entityplayer1.spawnInvulnerableTime = entityplayer1.level.purpurConfig.playerSpawnInvulnerableTicks; // Purpur
|
|
+
|
|
// CraftBukkit end
|
|
return entityplayer1;
|
|
}
|
|
@@ -1029,6 +1034,20 @@ public abstract class PlayerList {
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur Start
|
|
+ public void broadcastMiniMessage(@Nullable String message, boolean overlay) {
|
|
+ if (message != null && !message.isEmpty()) {
|
|
+ this.broadcastMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), overlay);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void broadcastMessage(@Nullable net.kyori.adventure.text.Component message, boolean overlay) {
|
|
+ if (message != null) {
|
|
+ this.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), overlay);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void broadcastAll(Packet<?> packet, ResourceKey<Level> dimension) {
|
|
Iterator iterator = this.players.iterator();
|
|
|
|
@@ -1132,6 +1151,7 @@ public abstract class PlayerList {
|
|
} else {
|
|
b0 = (byte) (24 + permissionLevel);
|
|
}
|
|
+ if (b0 < 28 && player.getBukkitEntity().hasPermission("purpur.debug.f3n")) b0 = 28; // Purpur
|
|
|
|
player.connection.send(new ClientboundEntityEventPacket(player, b0));
|
|
}
|
|
@@ -1140,6 +1160,27 @@ public abstract class PlayerList {
|
|
player.getBukkitEntity().recalculatePermissions(); // CraftBukkit
|
|
this.server.getCommands().sendCommands(player);
|
|
} // Paper
|
|
+
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) {
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkit = player.getBukkitEntity();
|
|
+ if (bukkit.hasPermission("purpur.enderchest.rows.six")) {
|
|
+ player.sixRowEnderchestSlotCount = 54;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.five")) {
|
|
+ player.sixRowEnderchestSlotCount = 45;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.four")) {
|
|
+ player.sixRowEnderchestSlotCount = 36;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.three")) {
|
|
+ player.sixRowEnderchestSlotCount = 27;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.two")) {
|
|
+ player.sixRowEnderchestSlotCount = 18;
|
|
+ } else if (bukkit.hasPermission("purpur.enderchest.rows.one")) {
|
|
+ player.sixRowEnderchestSlotCount = 9;
|
|
+ }
|
|
+ } else {
|
|
+ player.sixRowEnderchestSlotCount = -1;
|
|
+ }
|
|
+ //Purpur end
|
|
}
|
|
|
|
public boolean isWhiteListed(GameProfile profile) {
|
|
@@ -1201,7 +1242,7 @@ public abstract class PlayerList {
|
|
|
|
public void saveAll(int interval) {
|
|
io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main
|
|
- MinecraftTimings.savePlayers.startTiming(); // Paper
|
|
+ //MinecraftTimings.savePlayers.startTiming(); // Paper // Purpur
|
|
int numSaved = 0;
|
|
long now = MinecraftServer.currentTick;
|
|
for (int i = 0; i < this.players.size(); ++i) {
|
|
@@ -1212,7 +1253,7 @@ public abstract class PlayerList {
|
|
}
|
|
// Paper end
|
|
}
|
|
- MinecraftTimings.savePlayers.stopTiming(); // Paper
|
|
+ //MinecraftTimings.savePlayers.stopTiming(); // Paper // Purpur
|
|
return null; }); // Paper - ensure main
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/players/SleepStatus.java b/src/main/java/net/minecraft/server/players/SleepStatus.java
|
|
index 823efad652d8ff9e96b99375b102fef6f017716e..60f89d7c77a5e792e21e93e35ed1670bd565799a 100644
|
|
--- a/src/main/java/net/minecraft/server/players/SleepStatus.java
|
|
+++ b/src/main/java/net/minecraft/server/players/SleepStatus.java
|
|
@@ -19,7 +19,7 @@ public class SleepStatus {
|
|
|
|
public boolean areEnoughDeepSleeping(int percentage, List<ServerPlayer> players) {
|
|
// CraftBukkit start
|
|
- int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping; }).count();
|
|
+ int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping || (eh.level.purpurConfig.idleTimeoutCountAsSleeping && eh.isAfk()); }).count(); // Purpur
|
|
boolean anyDeepSleep = players.stream().anyMatch(Player::isSleepingLongEnough);
|
|
|
|
return anyDeepSleep && j >= this.sleepersNeeded(percentage);
|
|
@@ -52,7 +52,7 @@ public class SleepStatus {
|
|
|
|
if (!entityplayer.isSpectator()) {
|
|
++this.activePlayers;
|
|
- if (entityplayer.isSleeping() || entityplayer.fauxSleeping) { // CraftBukkit
|
|
+ if ((entityplayer.isSleeping() || entityplayer.fauxSleeping) || (entityplayer.level.purpurConfig.idleTimeoutCountAsSleeping && entityplayer.isAfk())) { // CraftBukkit // Purpur
|
|
++this.sleepingPlayers;
|
|
}
|
|
// CraftBukkit start
|
|
diff --git a/src/main/java/net/minecraft/stats/ServerRecipeBook.java b/src/main/java/net/minecraft/stats/ServerRecipeBook.java
|
|
index d13ed3069e944d138442ea440ac3eaf8d44c18d3..29ac7f202aa23f7e6fcdc9829af3d59875c92d4e 100644
|
|
--- a/src/main/java/net/minecraft/stats/ServerRecipeBook.java
|
|
+++ b/src/main/java/net/minecraft/stats/ServerRecipeBook.java
|
|
@@ -122,6 +122,7 @@ public class ServerRecipeBook extends RecipeBook {
|
|
Optional<? extends Recipe<?>> optional = recipeManager.byKey(minecraftkey);
|
|
|
|
if (!optional.isPresent()) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressUnrecognizedRecipeErrors) // Purpur
|
|
ServerRecipeBook.LOGGER.error("Tried to load unrecognized recipe: {} removed now.", minecraftkey);
|
|
} else {
|
|
handler.accept((Recipe) optional.get());
|
|
diff --git a/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java b/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java
|
|
index 196c7331138fee2822c76aacd136f9da040e0049..c6c30d99399c5cde2b0ec2f320d81d952b422d78 100644
|
|
--- a/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java
|
|
+++ b/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java
|
|
@@ -55,7 +55,7 @@ public class ActiveProfiler implements ProfileCollector {
|
|
this.started = true;
|
|
this.path = "";
|
|
this.paths.clear();
|
|
- this.push("root");
|
|
+ //this.push("root"); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -64,7 +64,7 @@ public class ActiveProfiler implements ProfileCollector {
|
|
if (!this.started) {
|
|
LOGGER.error("Profiler tick already ended - missing startTick()?");
|
|
} else {
|
|
- this.pop();
|
|
+ //this.pop(); // Purpur
|
|
this.started = false;
|
|
if (!this.path.isEmpty()) {
|
|
LOGGER.error("Profiler tick ended before path was fully popped (remainder: '{}'). Mismatched push/pop?", LogUtils.defer(() -> {
|
|
@@ -93,7 +93,7 @@ public class ActiveProfiler implements ProfileCollector {
|
|
|
|
@Override
|
|
public void push(Supplier<String> locationGetter) {
|
|
- this.push(locationGetter.get());
|
|
+ //this.push(locationGetter.get()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -132,14 +132,14 @@ public class ActiveProfiler implements ProfileCollector {
|
|
|
|
@Override
|
|
public void popPush(String location) {
|
|
- this.pop();
|
|
- this.push(location);
|
|
+ //this.pop(); // Purpur
|
|
+ //this.push(location); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void popPush(Supplier<String> locationGetter) {
|
|
- this.pop();
|
|
- this.push(locationGetter);
|
|
+ //this.pop(); // Purpur
|
|
+ //this.push(locationGetter); // Purpur
|
|
}
|
|
|
|
private ActiveProfiler.PathEntry getCurrentEntry() {
|
|
diff --git a/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java b/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java
|
|
index 2e6e8eac987c4ef6b2dcd3de592d8a51d2b29792..863343a87fe34d72f04af89d75268b477b2adc7a 100644
|
|
--- a/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java
|
|
+++ b/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java
|
|
@@ -6,32 +6,44 @@ import net.minecraft.util.profiling.metrics.MetricCategory;
|
|
public interface ProfilerFiller {
|
|
String ROOT = "root";
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void startTick();
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void endTick();
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void push(String location);
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void push(Supplier<String> locationGetter);
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void pop();
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void popPush(String location);
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void popPush(Supplier<String> locationGetter);
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void markForCharting(MetricCategory type);
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
default void incrementCounter(String marker) {
|
|
- this.incrementCounter(marker, 1);
|
|
+ //this.incrementCounter(marker, 1); // Purpur
|
|
}
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void incrementCounter(String marker, int num);
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
default void incrementCounter(Supplier<String> markerGetter) {
|
|
- this.incrementCounter(markerGetter, 1);
|
|
+ //this.incrementCounter(markerGetter, 1); // Purpur
|
|
}
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // Purpur
|
|
void incrementCounter(Supplier<String> markerGetter, int num);
|
|
|
|
static ProfilerFiller tee(final ProfilerFiller a, final ProfilerFiller b) {
|
|
@@ -41,62 +53,62 @@ public interface ProfilerFiller {
|
|
return b == InactiveProfiler.INSTANCE ? a : new ProfilerFiller() {
|
|
@Override
|
|
public void startTick() {
|
|
- a.startTick();
|
|
- b.startTick();
|
|
+ //a.startTick(); // Purpur
|
|
+ //b.startTick(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void endTick() {
|
|
- a.endTick();
|
|
- b.endTick();
|
|
+ //a.endTick(); // Purpur
|
|
+ //b.endTick(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void push(String location) {
|
|
- a.push(location);
|
|
- b.push(location);
|
|
+ //a.push(location); // Purpur
|
|
+ //b.push(location); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void push(Supplier<String> locationGetter) {
|
|
- a.push(locationGetter);
|
|
- b.push(locationGetter);
|
|
+ //a.push(locationGetter); // Purpur
|
|
+ //b.push(locationGetter); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void markForCharting(MetricCategory type) {
|
|
- a.markForCharting(type);
|
|
- b.markForCharting(type);
|
|
+ //a.markForCharting(type); // Purpur
|
|
+ //b.markForCharting(type); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void pop() {
|
|
- a.pop();
|
|
- b.pop();
|
|
+ //a.pop(); // Purpur
|
|
+ //b.pop(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void popPush(String location) {
|
|
- a.popPush(location);
|
|
- b.popPush(location);
|
|
+ //a.popPush(location); // Purpur
|
|
+ //b.popPush(location); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void popPush(Supplier<String> locationGetter) {
|
|
- a.popPush(locationGetter);
|
|
- b.popPush(locationGetter);
|
|
+ //a.popPush(locationGetter); // Purpur
|
|
+ //b.popPush(locationGetter); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void incrementCounter(String marker, int num) {
|
|
- a.incrementCounter(marker, num);
|
|
- b.incrementCounter(marker, num);
|
|
+ //a.incrementCounter(marker, num); // Purpur
|
|
+ //b.incrementCounter(marker, num); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void incrementCounter(Supplier<String> markerGetter, int num) {
|
|
- a.incrementCounter(markerGetter, num);
|
|
- b.incrementCounter(markerGetter, num);
|
|
+ //a.incrementCounter(markerGetter, num); // Purpur
|
|
+ //b.incrementCounter(markerGetter, num); // Purpur
|
|
}
|
|
};
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/damagesource/CombatRules.java b/src/main/java/net/minecraft/world/damagesource/CombatRules.java
|
|
index ccbfcef3e83b1bef364447657bfd08a92d615cf6..aa2331c6df4e79d4bb0add071a0b11d2a3a08b88 100644
|
|
--- a/src/main/java/net/minecraft/world/damagesource/CombatRules.java
|
|
+++ b/src/main/java/net/minecraft/world/damagesource/CombatRules.java
|
|
@@ -11,12 +11,12 @@ public class CombatRules {
|
|
|
|
public static float getDamageAfterAbsorb(float damage, float armor, float armorToughness) {
|
|
float f = 2.0F + armorToughness / 4.0F;
|
|
- float g = Mth.clamp(armor - damage / f, armor * 0.2F, 20.0F);
|
|
+ float g = Mth.clamp(armor - damage / f, armor * 0.2F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur
|
|
return damage * (1.0F - g / 25.0F);
|
|
}
|
|
|
|
public static float getDamageAfterMagicAbsorb(float damageDealt, float protection) {
|
|
- float f = Mth.clamp(protection, 0.0F, 20.0F);
|
|
+ float f = Mth.clamp(protection, 0.0F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur
|
|
return damageDealt * (1.0F - f / 25.0F);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
|
|
index 93a1e990b0a6caae4143c2f9d09bfb368fa1d6db..615611fe372d6edaef56db058bbf2cf7641e3c26 100644
|
|
--- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java
|
|
+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
|
|
@@ -126,6 +126,15 @@ public class DamageSource {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public Component getLocalizedDeathMessage(String str, LivingEntity entity) {
|
|
+ net.kyori.adventure.text.Component name = io.papermc.paper.adventure.PaperAdventure.asAdventure(entity.getDisplayName());
|
|
+ net.kyori.adventure.text.minimessage.tag.resolver.TagResolver template = net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("player", name);
|
|
+ net.kyori.adventure.text.Component component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(str, template);
|
|
+ return io.papermc.paper.adventure.PaperAdventure.asVanilla(component);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public String getMsgId() {
|
|
return this.type().msgId();
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/effect/MobEffect.java b/src/main/java/net/minecraft/world/effect/MobEffect.java
|
|
index 2cc714585fc3790b70a7ad1ab8034543462e2b3b..22d7f04cefafa0115a4504e37380787777091b18 100644
|
|
--- a/src/main/java/net/minecraft/world/effect/MobEffect.java
|
|
+++ b/src/main/java/net/minecraft/world/effect/MobEffect.java
|
|
@@ -60,16 +60,16 @@ public class MobEffect {
|
|
public void applyEffectTick(LivingEntity entity, int amplifier) {
|
|
if (this == MobEffects.REGENERATION) {
|
|
if (entity.getHealth() < entity.getMaxHealth()) {
|
|
- entity.heal(1.0F, RegainReason.MAGIC_REGEN); // CraftBukkit
|
|
+ entity.heal(entity.level.purpurConfig.entityHealthRegenAmount, RegainReason.MAGIC_REGEN); // CraftBukkit // Purpur
|
|
}
|
|
} else if (this == MobEffects.POISON) {
|
|
- if (entity.getHealth() > 1.0F) {
|
|
- entity.hurt(entity.damageSources().poison, 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON
|
|
+ if (entity.getHealth() > entity.level.purpurConfig.entityMinimalHealthPoison) { // Purpur
|
|
+ entity.hurt(entity.damageSources().poison, entity.level.purpurConfig.entityPoisonDegenerationAmount); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON // Purpur
|
|
}
|
|
} else if (this == MobEffects.WITHER) {
|
|
- entity.hurt(entity.damageSources().wither(), 1.0F);
|
|
+ entity.hurt(entity.damageSources().wither(), entity.level.purpurConfig.entityWitherDegenerationAmount); // Purpur
|
|
} else if (this == MobEffects.HUNGER && entity instanceof Player) {
|
|
- ((Player) entity).causeFoodExhaustion(0.005F * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent
|
|
+ ((Player) entity).causeFoodExhaustion(entity.level.purpurConfig.humanHungerExhaustionAmount * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent // Purpur
|
|
} else if (this == MobEffects.SATURATION && entity instanceof Player) {
|
|
if (!entity.level.isClientSide) {
|
|
// CraftBukkit start
|
|
@@ -79,7 +79,7 @@ public class MobEffect {
|
|
org.bukkit.event.entity.FoodLevelChangeEvent event = CraftEventFactory.callFoodLevelChangeEvent(entityhuman, amplifier + 1 + oldFoodLevel);
|
|
|
|
if (!event.isCancelled()) {
|
|
- entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F);
|
|
+ entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, entity.level.purpurConfig.humanSaturationRegenAmount); // Purpur
|
|
}
|
|
|
|
((ServerPlayer) entityhuman).connection.send(new ClientboundSetHealthPacket(((ServerPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel));
|
|
diff --git a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java
|
|
index 14fab63346d56c72cd7534a04760efd10eef4295..745e792482f61c571e2efbd4200dd1bdaef6e474 100644
|
|
--- a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java
|
|
+++ b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java
|
|
@@ -14,6 +14,7 @@ import net.minecraft.util.ExtraCodecs;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import org.slf4j.Logger;
|
|
+import org.bukkit.NamespacedKey;
|
|
|
|
public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
@@ -25,6 +26,7 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
private boolean visible;
|
|
private boolean showIcon;
|
|
@Nullable
|
|
+ private NamespacedKey key; // Purpur - add key
|
|
private MobEffectInstance hiddenEffect;
|
|
private final Optional<MobEffectInstance.FactorData> factorData;
|
|
|
|
@@ -44,17 +46,36 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
this(type, duration, amplifier, ambient, visible, visible);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean visible, @Nullable NamespacedKey key) {
|
|
+ this(type, duration, amplifier, ambient, visible, visible, key);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon) {
|
|
- this(type, duration, amplifier, ambient, showParticles, showIcon, (MobEffectInstance)null, type.createFactorData());
|
|
+ // Purpur start
|
|
+ this(type, duration, amplifier, ambient, showParticles, showIcon, (MobEffectInstance)null, type.createFactorData(), (NamespacedKey)null);
|
|
+ }
|
|
+
|
|
+ public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, @Nullable NamespacedKey key) {
|
|
+ this(type, duration, amplifier, ambient, showParticles, showIcon, (MobEffectInstance)null, type.createFactorData(), key);
|
|
+ // Purpur end
|
|
}
|
|
|
|
public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, @Nullable MobEffectInstance hiddenEffect, Optional<MobEffectInstance.FactorData> factorCalculationData) {
|
|
+ // Purpur start
|
|
+ this(type, duration, amplifier, ambient, showParticles, showIcon, hiddenEffect, factorCalculationData, (NamespacedKey) null);
|
|
+ }
|
|
+
|
|
+ public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, @Nullable MobEffectInstance hiddenEffect, Optional<MobEffectInstance.FactorData> factorCalculationData, @Nullable NamespacedKey key) {
|
|
+ // Purpur end
|
|
this.effect = type;
|
|
this.duration = duration;
|
|
this.amplifier = amplifier;
|
|
this.ambient = ambient;
|
|
this.visible = showParticles;
|
|
this.showIcon = showIcon;
|
|
+ this.key = key; // Purpur - add key
|
|
this.hiddenEffect = hiddenEffect;
|
|
this.factorData = factorCalculationData;
|
|
}
|
|
@@ -75,6 +96,7 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
this.ambient = that.ambient;
|
|
this.visible = that.visible;
|
|
this.showIcon = that.showIcon;
|
|
+ this.key = that.key; // Purpur - add key
|
|
}
|
|
|
|
public boolean update(MobEffectInstance that) {
|
|
@@ -120,6 +142,13 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
bl = true;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (that.key != this.key) {
|
|
+ this.key = that.key;
|
|
+ bl = true;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return bl;
|
|
}
|
|
|
|
@@ -163,6 +192,17 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
return this.showIcon;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public boolean hasKey() {
|
|
+ return this.key != null;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public NamespacedKey getKey() {
|
|
+ return this.key;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public boolean tick(LivingEntity entity, Runnable overwriteCallback) {
|
|
if (this.hasRemainingDuration()) {
|
|
int i = this.isInfiniteDuration() ? entity.tickCount : this.duration;
|
|
@@ -226,6 +266,12 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
string = string + ", Show Icon: false";
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (this.hasKey()) {
|
|
+ string = string + ", Key: " + this.key;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return string;
|
|
}
|
|
|
|
@@ -241,7 +287,7 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
return false;
|
|
} else {
|
|
MobEffectInstance mobEffectInstance = (MobEffectInstance)object;
|
|
- return this.duration == mobEffectInstance.duration && this.amplifier == mobEffectInstance.amplifier && this.ambient == mobEffectInstance.ambient && this.effect.equals(mobEffectInstance.effect);
|
|
+ return this.duration == mobEffectInstance.duration && this.amplifier == mobEffectInstance.amplifier && this.ambient == mobEffectInstance.ambient && this.effect.equals(mobEffectInstance.effect) && this.key == mobEffectInstance.key; // Purpur - add key
|
|
}
|
|
}
|
|
|
|
@@ -265,6 +311,11 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
nbt.putBoolean("Ambient", this.isAmbient());
|
|
nbt.putBoolean("ShowParticles", this.isVisible());
|
|
nbt.putBoolean("ShowIcon", this.showIcon());
|
|
+ // Purpur start
|
|
+ if (this.key != null) {
|
|
+ nbt.putString("Key", this.key.toString());
|
|
+ }
|
|
+ // Purpur end
|
|
if (this.hiddenEffect != null) {
|
|
CompoundTag compoundTag = new CompoundTag();
|
|
this.hiddenEffect.save(compoundTag);
|
|
@@ -299,6 +350,13 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
bl3 = nbt.getBoolean("ShowIcon");
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ NamespacedKey key = null;
|
|
+ if (nbt.contains("Key")) {
|
|
+ key = NamespacedKey.fromString(nbt.getString("Key"));
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
MobEffectInstance mobEffectInstance = null;
|
|
if (nbt.contains("HiddenEffect", 10)) {
|
|
mobEffectInstance = loadSpecifiedEffect(type, nbt.getCompound("HiddenEffect"));
|
|
@@ -311,7 +369,7 @@ public class MobEffectInstance implements Comparable<MobEffectInstance> {
|
|
optional = Optional.empty();
|
|
}
|
|
|
|
- return new MobEffectInstance(type, j, Math.max(i, 0), bl, bl2, bl3, mobEffectInstance, optional);
|
|
+ return new MobEffectInstance(type, j, Math.max(i, 0), bl, bl2, bl3, mobEffectInstance, optional, key); // Purpur - add key
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index b4ab2cfb7a5fa0d2efd1a759d754d5203aaac077..e71eca3ddbbeb3168dd73433b6d6ffe9f6755f77 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -156,7 +156,7 @@ import org.bukkit.plugin.PluginManager;
|
|
// CraftBukkit end
|
|
|
|
public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
-
|
|
+ public static javax.script.ScriptEngine scriptEngine = new javax.script.ScriptEngineManager().getEngineByName("rhino"); // Purpur
|
|
// CraftBukkit start
|
|
private static final int CURRENT_LEVEL = 2;
|
|
public boolean preserveMotion = true; // Paper - keep initial motion on first setPositionRotation
|
|
@@ -320,7 +320,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
public double xOld;
|
|
public double yOld;
|
|
public double zOld;
|
|
- private float maxUpStep;
|
|
+ public float maxUpStep; // Purpur - private -> public
|
|
public boolean noPhysics;
|
|
protected final RandomSource random;
|
|
public int tickCount;
|
|
@@ -362,7 +362,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
private final Set<String> tags;
|
|
private final double[] pistonDeltas;
|
|
private long pistonDeltasGameTime;
|
|
- private EntityDimensions dimensions;
|
|
+ protected EntityDimensions dimensions; // Purpur - private -> protected
|
|
private float eyeHeight;
|
|
public boolean isInPowderSnow;
|
|
public boolean wasInPowderSnow;
|
|
@@ -401,6 +401,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
private UUID originWorld;
|
|
public boolean freezeLocked = false; // Paper - Freeze Tick Lock API
|
|
public boolean collidingWithWorldBorder; // Paper
|
|
+ public @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API
|
|
|
|
public void setOrigin(@javax.annotation.Nonnull Location location) {
|
|
this.origin = location.toVector();
|
|
@@ -580,7 +581,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
this.bb = Entity.INITIAL_AABB;
|
|
this.stuckSpeedMultiplier = Vec3.ZERO;
|
|
this.nextStep = 1.0F;
|
|
- this.random = SHARED_RANDOM; // Paper
|
|
+ this.random = world == null || world.purpurConfig.entitySharedRandom ? SHARED_RANDOM : RandomSource.create(); // Paper // Purpur
|
|
this.remainingFireTicks = -this.getFireImmuneTicks();
|
|
this.fluidHeight = new Object2DoubleArrayMap(2);
|
|
this.fluidOnEyes = new HashSet();
|
|
@@ -825,7 +826,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
return;
|
|
}
|
|
// Pufferfish end - entity TTL
|
|
- this.level.getProfiler().push("entityBaseTick");
|
|
+ //this.level.getProfiler().push("entityBaseTick"); // Purpur
|
|
if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Update last hurt when ticking
|
|
this.feetBlockState = null;
|
|
if (this.isPassenger() && this.getVehicle().isRemoved()) {
|
|
@@ -886,7 +887,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
|
|
this.firstTick = false;
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
public void setSharedFlagOnFire(boolean onFire) {
|
|
@@ -895,10 +896,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
|
|
public void checkOutOfWorld() {
|
|
// Paper start - Configurable nether ceiling damage
|
|
- if (this.getY() < (double) (this.level.getMinBuildHeight() - 64) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER
|
|
+ if (this.getY() < (double) (this.level.getMinBuildHeight() + level.purpurConfig.voidDamageHeight) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER // Purpur
|
|
&& this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v)
|
|
&& (!(this instanceof Player player) || !player.getAbilities().invulnerable))) {
|
|
// Paper end
|
|
+ if (this.level.purpurConfig.teleportOnNetherCeilingDamage && this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this instanceof ServerPlayer player) player.teleport(MCUtil.toLocation(this.level, this.level.getSharedSpawnPos())); else // Purpur
|
|
this.outOfWorld();
|
|
}
|
|
|
|
@@ -1060,7 +1062,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
}
|
|
|
|
- this.level.getProfiler().push("move");
|
|
+ //this.level.getProfiler().push("move"); // Purpur
|
|
if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) {
|
|
movement = movement.multiply(this.stuckSpeedMultiplier);
|
|
this.stuckSpeedMultiplier = Vec3.ZERO;
|
|
@@ -1069,7 +1071,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
// Paper start - ignore movement changes while inactive.
|
|
if (isTemporarilyActive && !(this instanceof ItemEntity || this instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) && movement == getDeltaMovement() && movementType == MoverType.SELF) {
|
|
setDeltaMovement(Vec3.ZERO);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
return;
|
|
}
|
|
// Paper end
|
|
@@ -1090,8 +1092,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
this.setPos(this.getX() + vec3d1.x, this.getY() + vec3d1.y, this.getZ() + vec3d1.z);
|
|
}
|
|
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("rest");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("rest"); // Purpur
|
|
boolean flag = !Mth.equal(movement.x, vec3d1.x);
|
|
boolean flag1 = !Mth.equal(movement.z, vec3d1.z);
|
|
|
|
@@ -1110,7 +1112,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
|
|
this.checkFallDamage(vec3d1.y, this.onGround, iblockdata, blockposition);
|
|
if (this.isRemoved()) {
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
} else {
|
|
if (this.horizontalCollision) {
|
|
Vec3 vec3d2 = this.getDeltaMovement();
|
|
@@ -1251,7 +1253,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
this.setRemainingFireTicks(-this.getFireImmuneTicks());
|
|
}
|
|
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
}
|
|
// Paper start - detailed watchdog information
|
|
@@ -1678,7 +1680,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
|
|
public boolean fireImmune() {
|
|
- return this.getType().fireImmune();
|
|
+ return this.immuneToFire != null ? immuneToFire : this.getType().fireImmune(); // Purpur - add fire immune API
|
|
}
|
|
|
|
public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) {
|
|
@@ -1747,7 +1749,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
return this.isInWater() || flag;
|
|
}
|
|
|
|
- void updateInWaterStateAndDoWaterCurrentPushing() {
|
|
+ public void updateInWaterStateAndDoWaterCurrentPushing() { // Purpur - package-private -> public
|
|
Entity entity = this.getVehicle();
|
|
|
|
if (entity instanceof Boat) {
|
|
@@ -2340,6 +2342,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
nbt.putBoolean("Paper.FreezeLock", true);
|
|
}
|
|
// Paper end
|
|
+ // Purpur start
|
|
+ if (immuneToFire != null) {
|
|
+ nbt.putBoolean("Purpur.FireImmune", immuneToFire);
|
|
+ }
|
|
+ // Purpur end
|
|
return nbt;
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT");
|
|
@@ -2508,6 +2515,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
freezeLocked = nbt.getBoolean("Paper.FreezeLock");
|
|
}
|
|
// Paper end
|
|
+ // Purpur start
|
|
+ if (nbt.contains("Purpur.FireImmune")) {
|
|
+ immuneToFire = nbt.getBoolean("Purpur.FireImmune");
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT");
|
|
@@ -2824,6 +2836,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
this.passengers = ImmutableList.copyOf(list);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (isRidable() && this.passengers.get(0) == entity && entity instanceof Player player) {
|
|
+ onMount(player);
|
|
+ this.rider = player;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.gameEvent(GameEvent.ENTITY_MOUNT, entity);
|
|
}
|
|
return true; // CraftBukkit
|
|
@@ -2865,6 +2884,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
return false;
|
|
}
|
|
// Spigot end
|
|
+
|
|
+ // Purpur start
|
|
+ if (this.rider != null && this.passengers.get(0) == this.rider) {
|
|
+ onDismount(this.rider);
|
|
+ this.rider = null;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
|
|
this.passengers = ImmutableList.of();
|
|
} else {
|
|
@@ -2924,12 +2951,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
return Vec3.directionFromRotation(this.getRotationVector());
|
|
}
|
|
|
|
+ public BlockPos portalPos = BlockPos.ZERO; // Purpur
|
|
public void handleInsidePortal(BlockPos pos) {
|
|
if (this.isOnPortalCooldown()) {
|
|
+ if (!(level.purpurConfig.playerFixStuckPortal && this instanceof Player && !pos.equals(portalPos))) // Purpur
|
|
this.setPortalCooldown();
|
|
- } else {
|
|
+ } else if (level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer) { // Purpur
|
|
if (!this.level.isClientSide && !pos.equals(this.portalEntrancePos)) {
|
|
this.portalEntrancePos = pos.immutable();
|
|
+ portalPos = BlockPos.ZERO; // Purpur
|
|
}
|
|
|
|
this.isInsidePortal = true;
|
|
@@ -2947,7 +2977,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
ServerLevel worldserver1 = minecraftserver.getLevel(resourcekey);
|
|
|
|
if (true && !this.isPassenger() && this.portalTime++ >= i) { // CraftBukkit
|
|
- this.level.getProfiler().push("portal");
|
|
+ //this.level.getProfiler().push("portal"); // Purpur
|
|
this.portalTime = i;
|
|
// Paper start
|
|
io.papermc.paper.event.entity.EntityPortalReadyEvent event = new io.papermc.paper.event.entity.EntityPortalReadyEvent(this.getBukkitEntity(), worldserver1 == null ? null : worldserver1.getWorld(), org.bukkit.PortalType.NETHER);
|
|
@@ -2965,7 +2995,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
} // Paper
|
|
// CraftBukkit end
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
this.isInsidePortal = false;
|
|
@@ -2980,7 +3010,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
|
|
this.processPortalCooldown();
|
|
- this.tickEndPortal(); // Paper - make end portalling safe
|
|
+ if (this.level.purpurConfig.endPortalSafeTeleporting) this.tickEndPortal(); // Paper - make end portalling safe // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -3162,7 +3192,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
|
|
public int getMaxAirSupply() {
|
|
- return this.maxAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ return this.level == null? this.maxAirTicks : this.level.purpurConfig.drowningAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir() // Purpur
|
|
}
|
|
|
|
public int getAirSupply() {
|
|
@@ -3432,14 +3462,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
// Paper end
|
|
if (this.level instanceof ServerLevel && !this.isRemoved()) {
|
|
- this.level.getProfiler().push("changeDimension");
|
|
+ //this.level.getProfiler().push("changeDimension"); // Purpur
|
|
// CraftBukkit start
|
|
// this.decouple();
|
|
if (worldserver == null) {
|
|
return null;
|
|
}
|
|
// CraftBukkit end
|
|
- this.level.getProfiler().push("reposition");
|
|
+ //this.level.getProfiler().push("reposition"); // Purpur
|
|
PortalInfo shapedetectorshape = (location == null) ? this.findDimensionEntryPoint(worldserver) : new PortalInfo(new Vec3(location.x(), location.y(), location.z()), Vec3.ZERO, this.yRot, this.xRot, worldserver, null); // CraftBukkit
|
|
|
|
if (shapedetectorshape == null) {
|
|
@@ -3473,7 +3503,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
this.unRide();
|
|
// CraftBukkit end
|
|
|
|
- this.level.getProfiler().popPush("reloading");
|
|
+ //this.level.getProfiler().popPush("reloading"); // Purpur
|
|
// Paper start - Change lead drop timing to prevent dupe
|
|
if (this instanceof Mob) {
|
|
((Mob) this).dropLeash(true, true); // Paper drop lead
|
|
@@ -3496,10 +3526,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
|
|
this.removeAfterChangingDimensions();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
((ServerLevel) this.level).resetEmptyTime();
|
|
worldserver.resetEmptyTime();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
return entity;
|
|
}
|
|
} else {
|
|
@@ -3619,7 +3649,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
}
|
|
|
|
public boolean canChangeDimensions() {
|
|
- return !this.isPassenger() && !this.isVehicle() && isAlive() && valid; // Paper
|
|
+ return !this.isPassenger() && !this.isVehicle() && isAlive() && valid && (level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer); // Paper // Purpur
|
|
}
|
|
|
|
public float getBlockExplosionResistance(Explosion explosion, BlockGetter world, BlockPos pos, BlockState blockState, FluidState fluidState, float max) {
|
|
@@ -3916,6 +3946,20 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
return SlotAccess.NULL;
|
|
}
|
|
|
|
+ // Purpur Start
|
|
+ public void sendMiniMessage(@Nullable String message) {
|
|
+ if (message != null && !message.isEmpty()) {
|
|
+ this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void sendMessage(@Nullable net.kyori.adventure.text.Component message) {
|
|
+ if (message != null) {
|
|
+ this.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void sendSystemMessage(Component message) {}
|
|
|
|
@@ -4197,6 +4241,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
this.yRotO = this.getYRot();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public AABB getAxisForFluidCheck() {
|
|
+ return this.getBoundingBox().deflate(0.001D);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public boolean updateFluidHeightAndDoFluidPushing(TagKey<Fluid> tag, double speed) {
|
|
if (false && this.touchingUnloadedChunk()) { // Pufferfish - cost of a lookup here is the same cost as below, so skip
|
|
return false;
|
|
@@ -4725,4 +4775,64 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
return ((net.minecraft.server.level.ServerChunkCache) level.getChunkSource()).isPositionTicking(this);
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Nullable
|
|
+ private Player rider = null;
|
|
+
|
|
+ @Nullable
|
|
+ public Player getRider() {
|
|
+ return rider;
|
|
+ }
|
|
+
|
|
+ public boolean isRidable() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean isControllable() {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public void onMount(Player rider) {
|
|
+ if (this instanceof Mob) {
|
|
+ ((Mob) this).setTarget(null, null, false);
|
|
+ ((Mob) this).getNavigation().stop();
|
|
+ }
|
|
+ rider.setJumping(false); // fixes jump on mount
|
|
+ }
|
|
+
|
|
+ public void onDismount(Player player) {
|
|
+ }
|
|
+
|
|
+ public boolean onSpacebar() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean onClick(InteractionHand hand) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean processClick(InteractionHand hand) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean canSaveToDisk() {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ // Purpur start - copied from Mob
|
|
+ public boolean isSunBurnTick() {
|
|
+ if (this.level.isDay() && !this.level.isClientSide) {
|
|
+ float f = this.getLightLevelDependentMagicValue();
|
|
+ BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ());
|
|
+ boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow;
|
|
+
|
|
+ if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level.canSeeSky(blockposition)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java
|
|
index 72abebff2018cde2922e97ad6478f93da9aed3ec..412963d7af38a53b6010007278d959a5b11b83c3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/EntitySelector.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/EntitySelector.java
|
|
@@ -39,6 +39,7 @@ public final class EntitySelector {
|
|
return net.minecraft.util.Mth.clamp(serverPlayer.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= playerInsomniaTicks;
|
|
};
|
|
// Paper end
|
|
+ public static Predicate<Player> notAfk = (player) -> !player.isAfk(); // Purpur
|
|
|
|
private EntitySelector() {}
|
|
// Paper start
|
|
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
index 8af0918d3a62de58a4b2af55022c812bb0e46092..3fc26a8976f4bfa28c2c6a862aac997d5f721f51 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
@@ -308,13 +308,24 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
|
private Component description;
|
|
@Nullable
|
|
private ResourceLocation lootTable;
|
|
- private final EntityDimensions dimensions;
|
|
+ private EntityDimensions dimensions; // Purpur - remove final
|
|
+ public void setDimensions(EntityDimensions dimensions) { this.dimensions = dimensions; } // Purpur
|
|
private final FeatureFlagSet requiredFeatures;
|
|
|
|
private static <T extends Entity> EntityType<T> register(String id, EntityType.Builder type) { // CraftBukkit - decompile error
|
|
return (EntityType) Registry.register(BuiltInRegistries.ENTITY_TYPE, id, (EntityType<T>) type.build(id)); // CraftBukkit - decompile error
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static EntityType<?> getFromBukkitType(org.bukkit.entity.EntityType bukkitType) {
|
|
+ return getFromKey(new ResourceLocation(bukkitType.getKey().toString()));
|
|
+ }
|
|
+
|
|
+ public static EntityType<?> getFromKey(ResourceLocation location) {
|
|
+ return BuiltInRegistries.ENTITY_TYPE.get(location);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static ResourceLocation getKey(EntityType<?> type) {
|
|
return BuiltInRegistries.ENTITY_TYPE.getKey(type);
|
|
}
|
|
@@ -530,6 +541,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
|
return this.category;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public String getName() {
|
|
+ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath();
|
|
+ }
|
|
+
|
|
+ public String getTranslatedName() {
|
|
+ return getDescription().getString();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public String getDescriptionId() {
|
|
if (this.descriptionId == null) {
|
|
this.descriptionId = Util.makeDescriptionId("entity", BuiltInRegistries.ENTITY_TYPE.getKey(this));
|
|
@@ -591,6 +612,12 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
|
entity.load(nbt);
|
|
}, () -> {
|
|
EntityType.LOGGER.warn("Skipping Entity with id {}", nbt.getString("id"));
|
|
+ // Purpur start - log skipped entity's position
|
|
+ try {
|
|
+ ListTag pos = nbt.getList("Pos", 6);
|
|
+ EntityType.LOGGER.warn("Location: {} {},{},{}", world.getWorld().getName(), pos.getDouble(0), pos.getDouble(1), pos.getDouble(2));
|
|
+ } catch (Throwable ignore) {}
|
|
+ // Purpur end
|
|
});
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
|
|
index 89699aaccd45a5a928a97d1b3ad06f5de5b9fad1..b3b371ee4850c90a3142c3c96761b032e6de6af0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
|
|
@@ -306,7 +306,7 @@ public class ExperienceOrb extends Entity {
|
|
public void playerTouch(Player player) {
|
|
if (!this.level.isClientSide) {
|
|
if (player.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper
|
|
- player.takeXpDelay = CraftEventFactory.callPlayerXpCooldownEvent(player, 2, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2;
|
|
+ player.takeXpDelay = CraftEventFactory.callPlayerXpCooldownEvent(player, this.level.purpurConfig.playerExpPickupDelay, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2; // Purpur
|
|
player.take(this, 1);
|
|
int i = this.repairPlayerItems(player, this.value);
|
|
|
|
@@ -324,7 +324,7 @@ public class ExperienceOrb extends Entity {
|
|
}
|
|
|
|
private int repairPlayerItems(Player player, int amount) {
|
|
- Entry<EquipmentSlot, ItemStack> entry = EnchantmentHelper.getRandomItemWith(Enchantments.MENDING, player, ItemStack::isDamaged);
|
|
+ Entry<EquipmentSlot, ItemStack> entry = level.purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedEquipment(Enchantments.MENDING, player) : EnchantmentHelper.getRandomItemWith(Enchantments.MENDING, player, ItemStack::isDamaged); // Purpur
|
|
|
|
if (entry != null) {
|
|
ItemStack itemstack = (ItemStack) entry.getValue();
|
|
@@ -352,13 +352,15 @@ public class ExperienceOrb extends Entity {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
public int durabilityToXp(int repairAmount) {
|
|
- return repairAmount / 2;
|
|
+ return (int) (repairAmount / (2 * level.purpurConfig.mendingMultiplier));
|
|
}
|
|
|
|
public int xpToDurability(int experienceAmount) {
|
|
- return experienceAmount * 2;
|
|
+ return (int) ((experienceAmount * 2) * level.purpurConfig.mendingMultiplier);
|
|
}
|
|
+ // Purpur end
|
|
|
|
public int getValue() {
|
|
return this.value;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java
|
|
index c1e9b40a4a0f9cdc650caa88b5ea132e06ee2496..6f723171fa71d74b351b5cf0cd167bb6f7ca1691 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/GlowSquid.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java
|
|
@@ -18,11 +18,45 @@ import net.minecraft.world.level.block.Blocks;
|
|
|
|
public class GlowSquid extends Squid {
|
|
private static final EntityDataAccessor<Integer> DATA_DARK_TICKS_REMAINING = SynchedEntityData.defineId(GlowSquid.class, EntityDataSerializers.INT);
|
|
+ private static final net.minecraft.network.syncher.EntityDataAccessor<String> SQUID_COLOR = net.minecraft.network.syncher.SynchedEntityData.defineId(GlowSquid.class, net.minecraft.network.syncher.EntityDataSerializers.STRING); // Purpur
|
|
|
|
public GlowSquid(EntityType<? extends GlowSquid> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.glowSquidRidable;
|
|
+ }
|
|
+
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.glowSquidControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.glowSquidMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canFly() {
|
|
+ return this.level.purpurConfig.glowSquidsCanFly;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.glowSquidTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.glowSquidAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected ParticleOptions getInkParticle() {
|
|
return ParticleTypes.GLOW_SQUID_INK;
|
|
@@ -32,6 +66,7 @@ public class GlowSquid extends Squid {
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
this.entityData.define(DATA_DARK_TICKS_REMAINING, 0);
|
|
+ this.entityData.define(SQUID_COLOR, this.level.purpurConfig.glowSquidColorMode.getRandom(this.random).toString()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -58,12 +93,14 @@ public class GlowSquid extends Squid {
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putInt("DarkTicksRemaining", this.getDarkTicksRemaining());
|
|
+ nbt.putString("Colour", this.entityData.get(SQUID_COLOR)); // Purpur - key must match rainglow
|
|
}
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(CompoundTag nbt) {
|
|
super.readAdditionalSaveData(nbt);
|
|
this.setDarkTicks(nbt.getInt("DarkTicksRemaining"));
|
|
+ if (nbt.contains("Colour")) this.entityData.set(SQUID_COLOR, nbt.getString("Colour")); // Purpur - key must match rainglow
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
index f3d96caa83ef4a8083b78e3265282d4723e37d28..b67660cda74a4754d1701e746aca99bde868c150 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -216,9 +216,9 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
protected int deathScore;
|
|
public float lastHurt;
|
|
public boolean jumping;
|
|
- public float xxa;
|
|
- public float yya;
|
|
- public float zza;
|
|
+ public float xxa; public float getStrafeMot() { return xxa; } public void setStrafeMot(float strafe) { xxa = strafe; } // Purpur - OBFHELPER
|
|
+ public float yya; public float getVerticalMot() { return yya; } public void setVerticalMot(float vertical) { yya = vertical; } // Purpur - OBFHELPER
|
|
+ public float zza; public float getForwardMot() { return zza; } public void setForwardMot(float forward) { zza = forward; } // Purpur - OBFHELPER
|
|
protected int lerpSteps;
|
|
protected double lerpX;
|
|
protected double lerpY;
|
|
@@ -251,6 +251,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
private boolean skipDropExperience;
|
|
// CraftBukkit start
|
|
public int expToDrop;
|
|
+ public float safeFallDistance = 3.0F; // Purpur
|
|
public boolean forceDrops;
|
|
public ArrayList<org.bukkit.inventory.ItemStack> drops = new ArrayList<org.bukkit.inventory.ItemStack>();
|
|
public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
|
|
@@ -260,6 +261,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper
|
|
public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event
|
|
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper
|
|
+ protected boolean shouldBurnInDay = false; public boolean shouldBurnInDay() { return this.shouldBurnInDay; } public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } // Purpur
|
|
|
|
@Override
|
|
public float getBukkitYaw() {
|
|
@@ -284,7 +286,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.effectsDirty = true;
|
|
this.useItem = ItemStack.EMPTY;
|
|
this.lastClimbablePos = Optional.empty();
|
|
- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
|
|
+ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur
|
|
+ this.initAttributes(); // Purpur
|
|
this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit
|
|
// CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor
|
|
this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue());
|
|
@@ -300,6 +303,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.brain = this.makeBrain(new Dynamic(dynamicopsnbt, (Tag) dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), (Tag) dynamicopsnbt.emptyMap()))));
|
|
}
|
|
|
|
+ protected void initAttributes() {}// Purpur
|
|
+
|
|
public Brain<?> getBrain() {
|
|
return this.brain;
|
|
}
|
|
@@ -335,6 +340,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
public static AttributeSupplier.Builder createLivingAttributes() {
|
|
return AttributeSupplier.builder().add(Attributes.MAX_HEALTH).add(Attributes.KNOCKBACK_RESISTANCE).add(Attributes.MOVEMENT_SPEED).add(Attributes.ARMOR).add(Attributes.ARMOR_TOUGHNESS);
|
|
}
|
|
+ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur
|
|
|
|
@Override
|
|
protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) {
|
|
@@ -347,8 +353,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.tryAddSoulSpeed();
|
|
}
|
|
|
|
- if (!this.level.isClientSide && this.fallDistance > 3.0F && onGround) {
|
|
- float f = (float) Mth.ceil(this.fallDistance - 3.0F);
|
|
+ if (!this.level.isClientSide && this.fallDistance > this.safeFallDistance && onGround) { // Purpur
|
|
+ float f = (float) Mth.ceil(this.fallDistance - this.safeFallDistance); // Purpur
|
|
|
|
if (!state.isAir()) {
|
|
double d1 = Math.min((double) (0.2F + f / 15.0F), 2.5D);
|
|
@@ -387,7 +393,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
|
|
super.baseTick();
|
|
- this.level.getProfiler().push("livingEntityBaseTick");
|
|
+ //this.level.getProfiler().push("livingEntityBaseTick"); // Purpur
|
|
if (this.fireImmune() || this.level.isClientSide) {
|
|
this.clearFire();
|
|
}
|
|
@@ -405,6 +411,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
double d1 = this.level.getWorldBorder().getDamagePerBlock();
|
|
|
|
if (d1 > 0.0D) {
|
|
+ if (level.purpurConfig.teleportIfOutsideBorder && this instanceof ServerPlayer) { ((ServerPlayer) this).teleport(io.papermc.paper.util.MCUtil.toLocation(level, ((ServerLevel) level).getSharedSpawnPos())); return; } // Purpur
|
|
this.hurt(this.damageSources().inWall(), (float) Math.max(1, Mth.floor(-d0 * d1)));
|
|
}
|
|
}
|
|
@@ -416,7 +423,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
if (flag1) {
|
|
this.setAirSupply(this.decreaseAirSupply(this.getAirSupply()));
|
|
- if (this.getAirSupply() == -20) {
|
|
+ if (this.getAirSupply() == -this.level.purpurConfig.drowningDamageInterval) { // Purpur
|
|
this.setAirSupply(0);
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
|
|
@@ -428,7 +435,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.level.addParticle(ParticleTypes.BUBBLE, this.getX() + d2, this.getY() + d3, this.getZ() + d4, vec3d.x, vec3d.y, vec3d.z);
|
|
}
|
|
|
|
- this.hurt(this.damageSources().drown(), 2.0F);
|
|
+ this.hurt(this.damageSources().drown(), (float) this.level.purpurConfig.damageFromDrowning); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -489,7 +496,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.yHeadRotO = this.yHeadRot;
|
|
this.yRotO = this.getYRot();
|
|
this.xRotO = this.getXRot();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
public boolean canSpawnSoulSpeedParticle() {
|
|
@@ -781,6 +788,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> {
|
|
nbt.put("Brain", nbtbase);
|
|
});
|
|
+ nbt.putBoolean("Purpur.ShouldBurnInDay", shouldBurnInDay); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -865,6 +873,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain")));
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (nbt.contains("Purpur.ShouldBurnInDay")) {
|
|
+ shouldBurnInDay = nbt.getBoolean("Purpur.ShouldBurnInDay");
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
// CraftBukkit start
|
|
@@ -1010,9 +1023,31 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
|
EntityType<?> entitytypes = entity.getType();
|
|
|
|
- if (entitytypes == EntityType.SKELETON && itemstack.is(Items.SKELETON_SKULL) || entitytypes == EntityType.ZOMBIE && itemstack.is(Items.ZOMBIE_HEAD) || entitytypes == EntityType.PIGLIN && itemstack.is(Items.PIGLIN_HEAD) || entitytypes == EntityType.PIGLIN_BRUTE && itemstack.is(Items.PIGLIN_HEAD) || entitytypes == EntityType.CREEPER && itemstack.is(Items.CREEPER_HEAD)) {
|
|
- d0 *= 0.5D;
|
|
+ // Purpur start
|
|
+ if (entitytypes == EntityType.SKELETON && itemstack.is(Items.SKELETON_SKULL)) {
|
|
+ d0 *= entity.level.purpurConfig.skeletonHeadVisibilityPercent;
|
|
+ }
|
|
+ else if (entitytypes == EntityType.ZOMBIE && itemstack.is(Items.ZOMBIE_HEAD)) {
|
|
+ d0 *= entity.level.purpurConfig.zombieHeadVisibilityPercent;
|
|
+ }
|
|
+ else if (entitytypes == EntityType.CREEPER && itemstack.is(Items.CREEPER_HEAD)) {
|
|
+ d0 *= entity.level.purpurConfig.creeperHeadVisibilityPercent;
|
|
+ }
|
|
+ else if ((entitytypes == EntityType.PIGLIN || entitytypes == EntityType.PIGLIN_BRUTE) && itemstack.is(Items.PIGLIN_HEAD)) {
|
|
+ d0 *= entity.level.purpurConfig.piglinHeadVisibilityPercent;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ // Purpur start
|
|
+ if (entity instanceof LivingEntity entityliving) {
|
|
+ if (entityliving.hasEffect(MobEffects.BLINDNESS)) {
|
|
+ int amplifier = entityliving.getEffect(MobEffects.BLINDNESS).getAmplifier();
|
|
+ for (int i = 0; i < amplifier; i++) {
|
|
+ d0 *= this.level.purpurConfig.mobsBlindnessMultiplier;
|
|
+ }
|
|
+ }
|
|
}
|
|
+ // Purpur end
|
|
}
|
|
|
|
return d0;
|
|
@@ -1072,6 +1107,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
for (flag = false; iterator.hasNext(); flag = true) {
|
|
// CraftBukkit start
|
|
MobEffectInstance effect = (MobEffectInstance) iterator.next();
|
|
+ if (cause == EntityPotionEffectEvent.Cause.MILK && !this.level.purpurConfig.milkClearsBeneficialEffects && effect.getEffect().isBeneficial()) continue; // Purpur
|
|
EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED);
|
|
if (event.isCancelled()) {
|
|
continue;
|
|
@@ -1428,13 +1464,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
if (entity1 instanceof net.minecraft.world.entity.player.Player) {
|
|
net.minecraft.world.entity.player.Player entityhuman = (net.minecraft.world.entity.player.Player) entity1;
|
|
|
|
- this.lastHurtByPlayerTime = 100;
|
|
+ this.lastHurtByPlayerTime = this.level.purpurConfig.mobLastHurtByPlayerTime; // Purpur
|
|
this.lastHurtByPlayer = entityhuman;
|
|
} else if (entity1 instanceof Wolf) {
|
|
Wolf entitywolf = (Wolf) entity1;
|
|
|
|
if (entitywolf.isTame()) {
|
|
- this.lastHurtByPlayerTime = 100;
|
|
+ this.lastHurtByPlayerTime = this.level.purpurConfig.mobLastHurtByPlayerTime; // Purpur
|
|
LivingEntity entityliving2 = entitywolf.getOwner();
|
|
|
|
if (entityliving2 instanceof net.minecraft.world.entity.player.Player) {
|
|
@@ -1545,6 +1581,18 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.totemOfUndyingWorksInInventory && this instanceof ServerPlayer player && (itemstack == null || itemstack.getItem() != Items.TOTEM_OF_UNDYING) && player.getBukkitEntity().hasPermission("purpur.inventory_totem")) {
|
|
+ for (ItemStack item : player.getInventory().items) {
|
|
+ if (item.getItem() == Items.TOTEM_OF_UNDYING) {
|
|
+ itemstack1 = item;
|
|
+ itemstack = item.copy();
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
org.bukkit.inventory.EquipmentSlot handSlot = (hand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) : null;
|
|
EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot);
|
|
event.setCancelled(itemstack == null);
|
|
@@ -1705,7 +1753,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
boolean flag = false;
|
|
|
|
if (this.dead && adversary instanceof WitherBoss) { // Paper
|
|
- if (this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (this.level.purpurConfig.witherBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
BlockPos blockposition = this.blockPosition();
|
|
BlockState iblockdata = Blocks.WITHER_ROSE.defaultBlockState();
|
|
|
|
@@ -1751,6 +1799,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
this.dropEquipment(); // CraftBukkit - from below
|
|
if (this.shouldDropLoot() && this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
|
|
+ if (!(source.is(net.minecraft.world.damagesource.DamageTypes.CRAMMING) && level.purpurConfig.disableDropsOnCrammingDeath)) { // Purpur
|
|
this.dropFromLootTable(source, flag);
|
|
// Paper start
|
|
final boolean prev = this.clearEquipmentSlots;
|
|
@@ -1759,6 +1808,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
// Paper end
|
|
this.dropCustomDeathLoot(source, i, flag);
|
|
this.clearEquipmentSlots = prev; // Paper
|
|
+ } // Purpur
|
|
}
|
|
// CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment
|
|
org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, this.drops, () -> {
|
|
@@ -2014,7 +2064,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
MobEffectInstance mobeffect = this.getEffect(MobEffects.JUMP);
|
|
float f2 = mobeffect == null ? 0.0F : (float) (mobeffect.getAmplifier() + 1);
|
|
|
|
- return Mth.ceil((fallDistance - 3.0F - f2) * damageMultiplier);
|
|
+ return Mth.ceil((fallDistance - this.safeFallDistance - f2) * damageMultiplier); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -2237,6 +2287,20 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (damagesource.getEntity() instanceof net.minecraft.world.entity.player.Player player && damagesource.getEntity().level.purpurConfig.creativeOnePunch) {
|
|
+ if (player.isCreative()) {
|
|
+ double attackDamage = 0;
|
|
+ for (AttributeModifier modifier : player.getMainHandItem().getAttributeModifiers(EquipmentSlot.MAINHAND).get(Attributes.ATTACK_DAMAGE)) {
|
|
+ attackDamage += modifier.getAmount();
|
|
+ }
|
|
+ if (attackDamage == 0) {
|
|
+ this.setHealth(0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (f > 0 || !human) {
|
|
if (human) {
|
|
// PAIL: Be sure to drag all this code from the EntityHuman subclass each update.
|
|
@@ -2455,7 +2519,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
@Override
|
|
protected void outOfWorld() {
|
|
- this.hurt(this.damageSources().outOfWorld(), 4.0F);
|
|
+ this.hurt(this.damageSources().outOfWorld(), (float) level.purpurConfig.voidDamageDealt); // Purpur
|
|
}
|
|
|
|
protected void updateSwingTime() {
|
|
@@ -2652,7 +2716,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
|
|
protected long lastJumpTime = 0L; // Paper
|
|
- protected void jumpFromGround() {
|
|
+ public void jumpFromGround() { // Purpur - protected -> public
|
|
double d0 = (double) this.getJumpPower() + this.getJumpBoostPower();
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
// Paper start
|
|
@@ -2806,6 +2870,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
|
|
if (f3 > 0.0F) {
|
|
this.playSound(this.getFallDamageSound((int) f3), 1.0F, 1.0F);
|
|
+ if (level.purpurConfig.elytraKineticDamage) // Purpur
|
|
this.hurt(this.damageSources().flyIntoWall(), f3);
|
|
}
|
|
}
|
|
@@ -3028,10 +3093,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
|
|
this.run += (f3 - this.run) * 0.3F;
|
|
- this.level.getProfiler().push("headTurn");
|
|
+ //this.level.getProfiler().push("headTurn"); // Purpur
|
|
f2 = this.tickHeadTurn(f1, f2);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("rangeChecks");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("rangeChecks"); // Purpur
|
|
|
|
// Paper start - stop large pitch and yaw changes from crashing the server
|
|
this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F;
|
|
@@ -3043,7 +3108,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F;
|
|
// Paper end
|
|
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
this.animStep += f2;
|
|
if (this.isFallFlying()) {
|
|
++this.fallFlyTicks;
|
|
@@ -3332,19 +3397,19 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
|
|
this.setDeltaMovement(d4, d5, d6);
|
|
- this.level.getProfiler().push("ai");
|
|
+ //this.level.getProfiler().push("ai"); // Purpur
|
|
if (this.isImmobile()) {
|
|
this.jumping = false;
|
|
this.xxa = 0.0F;
|
|
this.zza = 0.0F;
|
|
} else if (this.isEffectiveAi()) {
|
|
- this.level.getProfiler().push("newAi");
|
|
+ //this.level.getProfiler().push("newAi"); // Purpur
|
|
this.serverAiStep();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("jump");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("jump"); // Purpur
|
|
if (this.jumping && this.isAffectedByFluids()) {
|
|
double d7;
|
|
|
|
@@ -3371,8 +3436,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.noJumpDelay = 0;
|
|
}
|
|
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("travel");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("travel"); // Purpur
|
|
this.xxa *= 0.98F;
|
|
this.zza *= 0.98F;
|
|
this.updateFallFlying();
|
|
@@ -3388,8 +3453,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
}
|
|
//SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper
|
|
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("freezing");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("freezing"); // Purpur
|
|
if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API
|
|
int i = this.getTicksFrozen();
|
|
|
|
@@ -3406,18 +3471,20 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
this.hurt(this.damageSources().freeze(), 1.0F);
|
|
}
|
|
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("push");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("push"); // Purpur
|
|
if (this.autoSpinAttackTicks > 0) {
|
|
--this.autoSpinAttackTicks;
|
|
this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox());
|
|
}
|
|
|
|
this.pushEntities();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
// Paper start
|
|
- if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
|
|
- if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
|
|
+ // Purpur start
|
|
+ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
|
|
+ if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
|
|
+ // Purpur end
|
|
Location from = new Location(this.level.getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
|
|
Location to = new Location (this.level.getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
|
io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
|
|
@@ -3427,12 +3494,48 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
|
|
}
|
|
}
|
|
+ // Purpur start
|
|
+ if (getRider() != null) {
|
|
+ getRider().resetLastActionTime();
|
|
+ if (((ServerLevel) level).hasRidableMoveEvent && this instanceof Mob) {
|
|
+ Location from = new Location(level.getWorld(), xo, yo, zo, this.yRotO, this.xRotO);
|
|
+ Location to = new Location(level.getWorld(), getX(), getY(), getZ(), this.getYRot(), this.getXRot());
|
|
+ org.purpurmc.purpur.event.entity.RidableMoveEvent event = new org.purpurmc.purpur.event.entity.RidableMoveEvent((org.bukkit.entity.Mob) getBukkitLivingEntity(), (Player) getRider().getBukkitEntity(), from, to.clone());
|
|
+ if (!event.callEvent()) {
|
|
+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
|
|
+ } else if (!to.equals(event.getTo())) {
|
|
+ absMoveTo(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
// Paper end
|
|
if (!this.level.isClientSide && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) {
|
|
this.hurt(this.damageSources().drown(), 1.0F);
|
|
}
|
|
|
|
+ // Purpur start - copied from Zombie
|
|
+ if (this.isAlive()) {
|
|
+ boolean flag = this.shouldBurnInDay() && this.isSunBurnTick();
|
|
+ if (flag) {
|
|
+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
|
+ if (!itemstack.isEmpty()) {
|
|
+ if (itemstack.isDamageableItem()) {
|
|
+ itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
|
|
+ if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
|
|
+ this.broadcastBreakEvent(EquipmentSlot.HEAD);
|
|
+ this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
|
|
+ }
|
|
+ }
|
|
+ flag = false;
|
|
+ }
|
|
+ if (flag) {
|
|
+ this.setSecondsOnFire(8);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
public boolean isSensitiveToWater() {
|
|
@@ -3453,7 +3556,16 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
int j = i / 10;
|
|
|
|
if (j % 2 == 0) {
|
|
- itemstack.hurtAndBreak(1, this, (entityliving) -> {
|
|
+ // Purpur start
|
|
+ int damage = level.purpurConfig.elytraDamagePerSecond;
|
|
+ if (level.purpurConfig.elytraDamageMultiplyBySpeed > 0) {
|
|
+ double speed = getDeltaMovement().lengthSqr();
|
|
+ if (speed > level.purpurConfig.elytraDamageMultiplyBySpeed) {
|
|
+ damage *= (int) speed;
|
|
+ }
|
|
+ }
|
|
+ itemstack.hurtAndBreak(damage, this, (entityliving) -> {
|
|
+ // Purpur end
|
|
entityliving.broadcastBreakEvent(EquipmentSlot.CHEST);
|
|
});
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
index 636e601b004a412d02e5be86e97d489b52c28e1b..8e2274f7dce34e0997356205cf96e46f8d41cca1 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
@@ -65,6 +65,7 @@ import net.minecraft.world.item.ProjectileWeaponItem;
|
|
import net.minecraft.world.item.SpawnEggItem;
|
|
import net.minecraft.world.item.SwordItem;
|
|
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
|
+import net.minecraft.world.item.enchantment.Enchantments;
|
|
import net.minecraft.world.level.GameRules;
|
|
import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.level.Level;
|
|
@@ -133,6 +134,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
private BlockPos restrictCenter;
|
|
private float restrictRadius;
|
|
|
|
+ public int ticksSinceLastInteraction; // Purpur
|
|
public boolean aware = true; // CraftBukkit
|
|
|
|
protected Mob(EntityType<? extends Mob> type, Level world) {
|
|
@@ -146,8 +148,8 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
this.restrictRadius = -1.0F;
|
|
this.goalSelector = new GoalSelector(world.getProfilerSupplier());
|
|
this.targetSelector = new GoalSelector(world.getProfilerSupplier());
|
|
- this.lookControl = new LookControl(this);
|
|
- this.moveControl = new MoveControl(this);
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this); // Purpur
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this); // Purpur
|
|
this.jumpControl = new JumpControl(this);
|
|
this.bodyRotationControl = this.createBodyControl();
|
|
this.navigation = this.createNavigation(world);
|
|
@@ -319,6 +321,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
entityliving = null;
|
|
}
|
|
}
|
|
+ if (entityliving instanceof ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur
|
|
this.target = entityliving;
|
|
return true;
|
|
// CraftBukkit end
|
|
@@ -359,15 +362,35 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
@Override
|
|
public void baseTick() {
|
|
super.baseTick();
|
|
- this.level.getProfiler().push("mobBaseTick");
|
|
+ //this.level.getProfiler().push("mobBaseTick"); // Purpur
|
|
if (this.isAlive() && this.random.nextInt(1000) < this.ambientSoundTime++) {
|
|
this.resetAmbientSoundTime();
|
|
this.playAmbientSound();
|
|
}
|
|
|
|
- this.level.getProfiler().pop();
|
|
+ incrementTicksSinceLastInteraction(); // Purpur
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private void incrementTicksSinceLastInteraction() {
|
|
+ ++this.ticksSinceLastInteraction;
|
|
+ if (getRider() != null) {
|
|
+ this.ticksSinceLastInteraction = 0;
|
|
+ return;
|
|
+ }
|
|
+ if (this.level.purpurConfig.entityLifeSpan <= 0) {
|
|
+ return; // feature disabled
|
|
+ }
|
|
+ if (!this.removeWhenFarAway(0) || isPersistenceRequired() || requiresCustomPersistence() || hasCustomName()) {
|
|
+ return; // mob persistent
|
|
+ }
|
|
+ if (this.ticksSinceLastInteraction > this.level.purpurConfig.entityLifeSpan) {
|
|
+ this.discard();
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void playHurtSound(DamageSource source) {
|
|
this.resetAmbientSoundTime();
|
|
@@ -557,6 +580,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
}
|
|
|
|
nbt.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit
|
|
+ nbt.putInt("Purpur.ticksSinceLastInteraction", this.ticksSinceLastInteraction); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -627,6 +651,11 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
this.aware = nbt.getBoolean("Bukkit.Aware");
|
|
}
|
|
// CraftBukkit end
|
|
+ // Purpur start
|
|
+ if (nbt.contains("Purpur.ticksSinceLastInteraction")) {
|
|
+ this.ticksSinceLastInteraction = nbt.getInt("Purpur.ticksSinceLastInteraction");
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -670,8 +699,8 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
@Override
|
|
public void aiStep() {
|
|
super.aiStep();
|
|
- this.level.getProfiler().push("looting");
|
|
- if (!this.level.isClientSide && this.canPickUpLoot() && this.isAlive() && !this.dead && this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ //this.level.getProfiler().push("looting"); // Purpur
|
|
+ if (!this.level.isClientSide && this.canPickUpLoot() && this.isAlive() && !this.dead && (this.level.purpurConfig.entitiesPickUpLootBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) {
|
|
Vec3i baseblockposition = this.getPickupReach();
|
|
List<ItemEntity> list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ()));
|
|
Iterator iterator = list.iterator();
|
|
@@ -690,7 +719,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
}
|
|
}
|
|
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
protected Vec3i getPickupReach() {
|
|
@@ -902,46 +931,46 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
return;
|
|
}
|
|
// Paper end
|
|
- this.level.getProfiler().push("sensing");
|
|
+ //this.level.getProfiler().push("sensing"); // Purpur
|
|
this.sensing.tick();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
int i = this.level.getServer().getTickCount() + this.getId();
|
|
|
|
if (i % 2 != 0 && this.tickCount > 1) {
|
|
- this.level.getProfiler().push("targetSelector");
|
|
+ //this.level.getProfiler().push("targetSelector"); // Purpur
|
|
if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
|
|
this.targetSelector.tickRunningGoals(false);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("goalSelector");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("goalSelector"); // Purpur
|
|
if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
|
|
this.goalSelector.tickRunningGoals(false);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
} else {
|
|
- this.level.getProfiler().push("targetSelector");
|
|
+ //this.level.getProfiler().push("targetSelector"); // Purpur
|
|
if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
|
|
this.targetSelector.tick();
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("goalSelector");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("goalSelector"); // Purpur
|
|
if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking
|
|
this.goalSelector.tick();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
- this.level.getProfiler().push("navigation");
|
|
+ //this.level.getProfiler().push("navigation"); // Purpur
|
|
this.navigation.tick();
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("mob tick");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("mob tick"); // Purpur
|
|
this.customServerAiStep();
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("controls");
|
|
- this.level.getProfiler().push("move");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("controls"); // Purpur
|
|
+ //this.level.getProfiler().push("move"); // Purpur
|
|
this.moveControl.tick();
|
|
- this.level.getProfiler().popPush("look");
|
|
+ //this.level.getProfiler().popPush("look"); // Purpur
|
|
this.lookControl.tick();
|
|
- this.level.getProfiler().popPush("jump");
|
|
+ //this.level.getProfiler().popPush("jump"); // Purpur
|
|
this.jumpControl.tick();
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
this.sendDebugPackets();
|
|
}
|
|
|
|
@@ -1163,6 +1192,12 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static @Nullable EquipmentSlot getSlotForDispenser(ItemStack itemstack) {
|
|
+ return EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BINDING_CURSE, itemstack) > 0 ? null : getEquipmentSlotForItem(itemstack);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Nullable
|
|
public static Item getEquipmentForSlot(EquipmentSlot equipmentSlot, int equipmentLevel) {
|
|
switch (equipmentSlot) {
|
|
@@ -1257,7 +1292,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
RandomSource randomsource = world.getRandom();
|
|
|
|
this.getAttribute(Attributes.FOLLOW_RANGE).addPermanentModifier(new AttributeModifier("Random spawn bonus", randomsource.triangle(0.0D, 0.11485000000000001D), AttributeModifier.Operation.MULTIPLY_BASE));
|
|
- if (randomsource.nextFloat() < 0.05F) {
|
|
+ if (randomsource.nextFloat() < world.getLevel().purpurConfig.entityLeftHandedChance) { // Purpur
|
|
this.setLeftHanded(true);
|
|
} else {
|
|
this.setLeftHanded(false);
|
|
@@ -1305,6 +1340,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
if (!this.isAlive()) {
|
|
return InteractionResult.PASS;
|
|
} else if (this.getLeashHolder() == player) {
|
|
+ if (hand == InteractionHand.OFF_HAND && (level.purpurConfig.villagerCanBeLeashed || level.purpurConfig.wanderingTraderCanBeLeashed) && this instanceof net.minecraft.world.entity.npc.AbstractVillager) return InteractionResult.CONSUME; // Purpur
|
|
// CraftBukkit start - fire PlayerUnleashEntityEvent
|
|
// Paper start - drop leash variable
|
|
org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.getAbilities().instabuild);
|
|
@@ -1378,7 +1414,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
protected void onOffspringSpawnedFromEgg(Player player, Mob child) {}
|
|
|
|
protected InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
|
|
public boolean isWithinRestriction() {
|
|
@@ -1684,6 +1720,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
this.setLastHurtMob(target);
|
|
}
|
|
|
|
+ if (target instanceof ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur
|
|
return flag;
|
|
}
|
|
|
|
@@ -1700,17 +1737,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
}
|
|
|
|
public boolean isSunBurnTick() {
|
|
- if (this.level.isDay() && !this.level.isClientSide) {
|
|
- float f = this.getLightLevelDependentMagicValue();
|
|
- BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ());
|
|
- boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow;
|
|
-
|
|
- if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level.canSeeSky(blockposition)) {
|
|
- return true;
|
|
- }
|
|
- }
|
|
-
|
|
- return false;
|
|
+ return super.isSunBurnTick(); // Purpur - moved contents to Entity
|
|
}
|
|
|
|
@Override
|
|
@@ -1754,4 +1781,56 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
|
|
return itemmonsteregg == null ? null : new ItemStack(itemmonsteregg);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public double getMaxY() {
|
|
+ return level.getHeight();
|
|
+ }
|
|
+
|
|
+ public InteractionResult tryRide(Player player, InteractionHand hand) {
|
|
+ return tryRide(player, hand, InteractionResult.PASS);
|
|
+ }
|
|
+
|
|
+ public InteractionResult tryRide(Player player, InteractionHand hand, InteractionResult result) {
|
|
+ if (!isRidable()) {
|
|
+ return result;
|
|
+ }
|
|
+ if (hand != InteractionHand.MAIN_HAND) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (player.isShiftKeyDown()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (!player.getItemInHand(hand).isEmpty()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (!passengers.isEmpty() || player.isPassenger()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (this instanceof TamableAnimal tamable) {
|
|
+ if (tamable.isTame() && !tamable.isOwnedBy(player)) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (!tamable.isTame() && !level.purpurConfig.untamedTamablesAreRidable) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ }
|
|
+ if (this instanceof AgeableMob ageable) {
|
|
+ if (ageable.isBaby() && !level.purpurConfig.babiesAreRidable) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ }
|
|
+ if (!player.getBukkitEntity().hasPermission("allow.ride." + net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getKey(getType()).getPath())) {
|
|
+ player.sendMiniMessage(org.purpurmc.purpur.PurpurConfig.cannotRideMob);
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ player.setYRot(this.getYRot());
|
|
+ player.setXRot(this.getXRot());
|
|
+ if (player.startRiding(this)) {
|
|
+ return InteractionResult.SUCCESS;
|
|
+ } else {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Shearable.java b/src/main/java/net/minecraft/world/entity/Shearable.java
|
|
index 5e8cc5cfac8888628c6d513148f41be09ca65a2c..a089fc61ec09be6b7490375489178dc6ba5a644b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Shearable.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Shearable.java
|
|
@@ -3,7 +3,13 @@ package net.minecraft.world.entity;
|
|
import net.minecraft.sounds.SoundSource;
|
|
|
|
public interface Shearable {
|
|
- void shear(SoundSource shearedSoundCategory);
|
|
+ // Purpur start
|
|
+ default void shear(SoundSource shearedSoundCategory) {
|
|
+ shear(shearedSoundCategory, 0);
|
|
+ }
|
|
+
|
|
+ void shear(SoundSource shearedSoundCategory, int looting);
|
|
+ // Purpur end
|
|
|
|
boolean readyForShearing();
|
|
}
|
|
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 e283eb57c25f7de222f9d09dca851169f5f6e488..210a0bee1227e4671909dd553ab22027cfc868fb 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
|
|
@@ -24,14 +24,21 @@ public class AttributeMap {
|
|
private final Set<AttributeInstance> dirtyAttributes = Sets.newHashSet();
|
|
private final AttributeSupplier supplier;
|
|
private final java.util.function.Function<Attribute, AttributeInstance> createInstance; // Pufferfish
|
|
+ private final net.minecraft.world.entity.LivingEntity entity; // Purpur
|
|
|
|
public AttributeMap(AttributeSupplier defaultAttributes) {
|
|
+ // Purpur start
|
|
+ this(defaultAttributes, null);
|
|
+ }
|
|
+ public AttributeMap(AttributeSupplier defaultAttributes, net.minecraft.world.entity.LivingEntity entity) {
|
|
+ this.entity = entity;
|
|
+ // Purpur end
|
|
this.supplier = defaultAttributes;
|
|
this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Pufferfish
|
|
}
|
|
|
|
private void onAttributeModified(AttributeInstance instance) {
|
|
- if (instance.getAttribute().isClientSyncable()) {
|
|
+ if (instance.getAttribute().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute()))) { // Purpur
|
|
this.dirtyAttributes.add(instance);
|
|
}
|
|
|
|
@@ -43,7 +50,7 @@ public class AttributeMap {
|
|
|
|
public Collection<AttributeInstance> getSyncableAttributes() {
|
|
return this.attributes.values().stream().filter((attribute) -> {
|
|
- return attribute.getAttribute().isClientSyncable();
|
|
+ return attribute.getAttribute().isClientSyncable() && (entity == null || entity.shouldSendAttribute(attribute.getAttribute())); // Purpur
|
|
}).collect(Collectors.toList());
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
|
|
index 8a720f9ae81d7ea856e28cb27a66adcf04bcb0eb..e0b70d9732a2b7d96999b7e4a497ffa1d8cf86a7 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
|
|
@@ -80,7 +80,88 @@ import org.slf4j.Logger;
|
|
|
|
public class DefaultAttributes {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
- private static final Map<EntityType<? extends LivingEntity>, AttributeSupplier> SUPPLIERS = ImmutableMap.<EntityType<? extends LivingEntity>, AttributeSupplier>builder().put(EntityType.ALLAY, Allay.createAttributes().build()).put(EntityType.ARMOR_STAND, LivingEntity.createLivingAttributes().build()).put(EntityType.AXOLOTL, Axolotl.createAttributes().build()).put(EntityType.BAT, Bat.createAttributes().build()).put(EntityType.BEE, Bee.createAttributes().build()).put(EntityType.BLAZE, Blaze.createAttributes().build()).put(EntityType.CAT, Cat.createAttributes().build()).put(EntityType.CAMEL, Camel.createAttributes().build()).put(EntityType.CAVE_SPIDER, CaveSpider.createCaveSpider().build()).put(EntityType.CHICKEN, Chicken.createAttributes().build()).put(EntityType.COD, AbstractFish.createAttributes().build()).put(EntityType.COW, Cow.createAttributes().build()).put(EntityType.CREEPER, Creeper.createAttributes().build()).put(EntityType.DOLPHIN, Dolphin.createAttributes().build()).put(EntityType.DONKEY, AbstractChestedHorse.createBaseChestedHorseAttributes().build()).put(EntityType.DROWNED, Zombie.createAttributes().build()).put(EntityType.ELDER_GUARDIAN, ElderGuardian.createAttributes().build()).put(EntityType.ENDERMAN, EnderMan.createAttributes().build()).put(EntityType.ENDERMITE, Endermite.createAttributes().build()).put(EntityType.ENDER_DRAGON, EnderDragon.createAttributes().build()).put(EntityType.EVOKER, Evoker.createAttributes().build()).put(EntityType.FOX, Fox.createAttributes().build()).put(EntityType.FROG, Frog.createAttributes().build()).put(EntityType.GHAST, Ghast.createAttributes().build()).put(EntityType.GIANT, Giant.createAttributes().build()).put(EntityType.GLOW_SQUID, GlowSquid.createAttributes().build()).put(EntityType.GOAT, Goat.createAttributes().build()).put(EntityType.GUARDIAN, Guardian.createAttributes().build()).put(EntityType.HOGLIN, Hoglin.createAttributes().build()).put(EntityType.HORSE, AbstractHorse.createBaseHorseAttributes().build()).put(EntityType.HUSK, Zombie.createAttributes().build()).put(EntityType.ILLUSIONER, Illusioner.createAttributes().build()).put(EntityType.IRON_GOLEM, IronGolem.createAttributes().build()).put(EntityType.LLAMA, Llama.createAttributes().build()).put(EntityType.MAGMA_CUBE, MagmaCube.createAttributes().build()).put(EntityType.MOOSHROOM, Cow.createAttributes().build()).put(EntityType.MULE, AbstractChestedHorse.createBaseChestedHorseAttributes().build()).put(EntityType.OCELOT, Ocelot.createAttributes().build()).put(EntityType.PANDA, Panda.createAttributes().build()).put(EntityType.PARROT, Parrot.createAttributes().build()).put(EntityType.PHANTOM, Monster.createMonsterAttributes().build()).put(EntityType.PIG, Pig.createAttributes().build()).put(EntityType.PIGLIN, Piglin.createAttributes().build()).put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build()).put(EntityType.PILLAGER, Pillager.createAttributes().build()).put(EntityType.PLAYER, Player.createAttributes().build()).put(EntityType.POLAR_BEAR, PolarBear.createAttributes().build()).put(EntityType.PUFFERFISH, AbstractFish.createAttributes().build()).put(EntityType.RABBIT, Rabbit.createAttributes().build()).put(EntityType.RAVAGER, Ravager.createAttributes().build()).put(EntityType.SALMON, AbstractFish.createAttributes().build()).put(EntityType.SHEEP, Sheep.createAttributes().build()).put(EntityType.SHULKER, Shulker.createAttributes().build()).put(EntityType.SILVERFISH, Silverfish.createAttributes().build()).put(EntityType.SKELETON, AbstractSkeleton.createAttributes().build()).put(EntityType.SKELETON_HORSE, SkeletonHorse.createAttributes().build()).put(EntityType.SLIME, Monster.createMonsterAttributes().build()).put(EntityType.SNIFFER, Sniffer.createAttributes().build()).put(EntityType.SNOW_GOLEM, SnowGolem.createAttributes().build()).put(EntityType.SPIDER, Spider.createAttributes().build()).put(EntityType.SQUID, Squid.createAttributes().build()).put(EntityType.STRAY, AbstractSkeleton.createAttributes().build()).put(EntityType.STRIDER, Strider.createAttributes().build()).put(EntityType.TADPOLE, Tadpole.createAttributes().build()).put(EntityType.TRADER_LLAMA, Llama.createAttributes().build()).put(EntityType.TROPICAL_FISH, AbstractFish.createAttributes().build()).put(EntityType.TURTLE, Turtle.createAttributes().build()).put(EntityType.VEX, Vex.createAttributes().build()).put(EntityType.VILLAGER, Villager.createAttributes().build()).put(EntityType.VINDICATOR, Vindicator.createAttributes().build()).put(EntityType.WARDEN, Warden.createAttributes().build()).put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build()).put(EntityType.WITCH, Witch.createAttributes().build()).put(EntityType.WITHER, WitherBoss.createAttributes().build()).put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build()).put(EntityType.WOLF, Wolf.createAttributes().build()).put(EntityType.ZOGLIN, Zoglin.createAttributes().build()).put(EntityType.ZOMBIE, Zombie.createAttributes().build()).put(EntityType.ZOMBIE_HORSE, ZombieHorse.createAttributes().build()).put(EntityType.ZOMBIE_VILLAGER, Zombie.createAttributes().build()).put(EntityType.ZOMBIFIED_PIGLIN, ZombifiedPiglin.createAttributes().build()).build();
|
|
+ private static final Map<EntityType<? extends LivingEntity>, AttributeSupplier> SUPPLIERS = ImmutableMap.<EntityType<? extends LivingEntity>, AttributeSupplier>builder()
|
|
+ .put(EntityType.ALLAY, Allay.createAttributes().build())
|
|
+ .put(EntityType.ARMOR_STAND, LivingEntity.createLivingAttributes().build())
|
|
+ .put(EntityType.AXOLOTL, Axolotl.createAttributes().build())
|
|
+ .put(EntityType.BAT, Bat.createAttributes().build())
|
|
+ .put(EntityType.BEE, Bee.createAttributes().build())
|
|
+ .put(EntityType.BLAZE, Blaze.createAttributes().build())
|
|
+ .put(EntityType.CAT, Cat.createAttributes().build())
|
|
+ .put(EntityType.CAMEL, Camel.createAttributes().build())
|
|
+ .put(EntityType.CAVE_SPIDER, CaveSpider.createCaveSpider().build())
|
|
+ .put(EntityType.CHICKEN, Chicken.createAttributes().build())
|
|
+ .put(EntityType.COD, AbstractFish.createAttributes().build())
|
|
+ .put(EntityType.COW, Cow.createAttributes().build())
|
|
+ .put(EntityType.CREEPER, Creeper.createAttributes().build())
|
|
+ .put(EntityType.DOLPHIN, Dolphin.createAttributes().build())
|
|
+ .put(EntityType.DONKEY, AbstractChestedHorse.createBaseChestedHorseAttributes().build())
|
|
+ .put(EntityType.DROWNED, Zombie.createAttributes().build())
|
|
+ .put(EntityType.ELDER_GUARDIAN, ElderGuardian.createAttributes().build())
|
|
+ .put(EntityType.ENDERMAN, EnderMan.createAttributes().build())
|
|
+ .put(EntityType.ENDERMITE, Endermite.createAttributes().build())
|
|
+ .put(EntityType.ENDER_DRAGON, EnderDragon.createAttributes().build())
|
|
+ .put(EntityType.EVOKER, Evoker.createAttributes().build())
|
|
+ .put(EntityType.FOX, Fox.createAttributes().build())
|
|
+ .put(EntityType.FROG, Frog.createAttributes().build())
|
|
+ .put(EntityType.GHAST, Ghast.createAttributes().build())
|
|
+ .put(EntityType.GIANT, Giant.createAttributes().build())
|
|
+ .put(EntityType.GLOW_SQUID, GlowSquid.createAttributes().build())
|
|
+ .put(EntityType.GOAT, Goat.createAttributes().build())
|
|
+ .put(EntityType.GUARDIAN, Guardian.createAttributes().build())
|
|
+ .put(EntityType.HOGLIN, Hoglin.createAttributes().build())
|
|
+ .put(EntityType.HORSE, AbstractHorse.createBaseHorseAttributes().build())
|
|
+ .put(EntityType.HUSK, Zombie.createAttributes().build())
|
|
+ .put(EntityType.ILLUSIONER, Illusioner.createAttributes().build())
|
|
+ .put(EntityType.IRON_GOLEM, IronGolem.createAttributes().build())
|
|
+ .put(EntityType.LLAMA, Llama.createAttributes().build())
|
|
+ .put(EntityType.MAGMA_CUBE, MagmaCube.createAttributes().build())
|
|
+ .put(EntityType.MOOSHROOM, Cow.createAttributes().build())
|
|
+ .put(EntityType.MULE, AbstractChestedHorse.createBaseChestedHorseAttributes().build())
|
|
+ .put(EntityType.OCELOT, Ocelot.createAttributes().build())
|
|
+ .put(EntityType.PANDA, Panda.createAttributes().build())
|
|
+ .put(EntityType.PARROT, Parrot.createAttributes().build())
|
|
+ .put(EntityType.PHANTOM, net.minecraft.world.entity.monster.Phantom.createAttributes().build()) // Purpur
|
|
+ .put(EntityType.PIG, Pig.createAttributes().build())
|
|
+ .put(EntityType.PIGLIN, Piglin.createAttributes().build())
|
|
+ .put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build())
|
|
+ .put(EntityType.PILLAGER, Pillager.createAttributes().build())
|
|
+ .put(EntityType.PLAYER, Player.createAttributes().build())
|
|
+ .put(EntityType.POLAR_BEAR, PolarBear.createAttributes().build())
|
|
+ .put(EntityType.PUFFERFISH, AbstractFish.createAttributes().build())
|
|
+ .put(EntityType.RABBIT, Rabbit.createAttributes().build())
|
|
+ .put(EntityType.RAVAGER, Ravager.createAttributes().build())
|
|
+ .put(EntityType.SALMON, AbstractFish.createAttributes().build())
|
|
+ .put(EntityType.SHEEP, Sheep.createAttributes().build())
|
|
+ .put(EntityType.SHULKER, Shulker.createAttributes().build())
|
|
+ .put(EntityType.SILVERFISH, Silverfish.createAttributes().build())
|
|
+ .put(EntityType.SKELETON, AbstractSkeleton.createAttributes().build())
|
|
+ .put(EntityType.SKELETON_HORSE, SkeletonHorse.createAttributes().build())
|
|
+ .put(EntityType.SLIME, Monster.createMonsterAttributes().build())
|
|
+ .put(EntityType.SNIFFER, Sniffer.createAttributes().build())
|
|
+ .put(EntityType.SNOW_GOLEM, SnowGolem.createAttributes().build())
|
|
+ .put(EntityType.SPIDER, Spider.createAttributes().build())
|
|
+ .put(EntityType.SQUID, Squid.createAttributes().build())
|
|
+ .put(EntityType.STRAY, AbstractSkeleton.createAttributes().build())
|
|
+ .put(EntityType.STRIDER, Strider.createAttributes().build())
|
|
+ .put(EntityType.TADPOLE, Tadpole.createAttributes().build())
|
|
+ .put(EntityType.TRADER_LLAMA, Llama.createAttributes().build())
|
|
+ .put(EntityType.TROPICAL_FISH, AbstractFish.createAttributes().build())
|
|
+ .put(EntityType.TURTLE, Turtle.createAttributes().build())
|
|
+ .put(EntityType.VEX, Vex.createAttributes().build())
|
|
+ .put(EntityType.VILLAGER, Villager.createAttributes().build())
|
|
+ .put(EntityType.VINDICATOR, Vindicator.createAttributes().build())
|
|
+ .put(EntityType.WARDEN, Warden.createAttributes().build())
|
|
+ .put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build())
|
|
+ .put(EntityType.WITCH, Witch.createAttributes().build())
|
|
+ .put(EntityType.WITHER, WitherBoss.createAttributes().build())
|
|
+ .put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build())
|
|
+ .put(EntityType.WOLF, Wolf.createAttributes().build())
|
|
+ .put(EntityType.ZOGLIN, Zoglin.createAttributes().build())
|
|
+ .put(EntityType.ZOMBIE, Zombie.createAttributes().build())
|
|
+ .put(EntityType.ZOMBIE_HORSE, ZombieHorse.createAttributes().build())
|
|
+ .put(EntityType.ZOMBIE_VILLAGER, Zombie.createAttributes().build())
|
|
+ .put(EntityType.ZOMBIFIED_PIGLIN, ZombifiedPiglin.createAttributes().build()).build();
|
|
|
|
public static AttributeSupplier getSupplier(EntityType<? extends LivingEntity> type) {
|
|
return SUPPLIERS.get(type);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java b/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java
|
|
index f0703302e7dbbda88de8c648d20d87c55ed9b1e0..a913ebabaa5f443afa987b972355a8f8d1723c78 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java
|
|
@@ -29,6 +29,7 @@ public class RangedAttribute extends Attribute {
|
|
|
|
@Override
|
|
public double sanitizeValue(double value) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.clampAttributes) return Double.isNaN(value) ? this.minValue : value; // Purpur
|
|
return Double.isNaN(value) ? this.minValue : Mth.clamp(value, this.minValue, this.maxValue);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
|
|
index 57ef7fbba3028c28231abf7b7ae78aa019323536..651c156dc8a5aad04d461add02e22147af657d07 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
|
|
@@ -58,9 +58,9 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro
|
|
this.status = Behavior.Status.RUNNING;
|
|
int i = this.minDuration + world.getRandom().nextInt(this.maxDuration + 1 - this.minDuration);
|
|
this.endTimestamp = time + (long)i;
|
|
- this.timing.startTiming(); // Paper - behavior timings
|
|
+ //this.timing.startTiming(); // Paper - behavior timings // Purpur
|
|
this.start(world, entity, time);
|
|
- this.timing.stopTiming(); // Paper - behavior timings
|
|
+ //this.timing.stopTiming(); // Paper - behavior timings // Purpur
|
|
return true;
|
|
} else {
|
|
return false;
|
|
@@ -72,13 +72,13 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro
|
|
|
|
@Override
|
|
public final void tickOrStop(ServerLevel world, E entity, long time) {
|
|
- this.timing.startTiming(); // Paper - behavior timings
|
|
+ //this.timing.startTiming(); // Paper - behavior timings // Purpur
|
|
if (!this.timedOut(time) && this.canStillUse(world, entity, time)) {
|
|
this.tick(world, entity, time);
|
|
} else {
|
|
this.doStop(world, entity, time);
|
|
}
|
|
- this.timing.stopTiming(); // Paper - behavior timings
|
|
+ //this.timing.stopTiming(); // Paper - behavior timings // Purpur
|
|
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java
|
|
index 7ad71f2c139c2288b49d6b0fde3f8b8013f5e095..2dca8e45b9b1f5451db2734cba4c2b03c9dd303b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java
|
|
@@ -34,17 +34,19 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
private long nextOkStartTime;
|
|
private int timeWorkedSoFar;
|
|
private final List<BlockPos> validFarmlandAroundVillager = Lists.newArrayList();
|
|
+ private boolean clericWartFarmer = false; // Purpur
|
|
|
|
public HarvestFarmland() {
|
|
super(ImmutableMap.of(MemoryModuleType.LOOK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.SECONDARY_JOB_SITE, MemoryStatus.VALUE_PRESENT));
|
|
}
|
|
|
|
protected boolean checkExtraStartConditions(ServerLevel world, Villager entity) {
|
|
- if (!world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!world.purpurConfig.villagerBypassMobGriefing && !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
- } else if (entity.getVillagerData().getProfession() != VillagerProfession.FARMER) {
|
|
+ } else if (entity.getVillagerData().getProfession() != VillagerProfession.FARMER && !(world.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC)) { // Purpur
|
|
return false;
|
|
} else {
|
|
+ if (!this.clericWartFarmer && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC) this.clericWartFarmer = true; // Purpur
|
|
BlockPos.MutableBlockPos blockposition_mutableblockposition = entity.blockPosition().mutable();
|
|
|
|
this.validFarmlandAroundVillager.clear();
|
|
@@ -75,6 +77,7 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
Block block = iblockdata.getBlock();
|
|
Block block1 = world.getBlockState(pos.below()).getBlock();
|
|
|
|
+ if (this.clericWartFarmer) return block == Blocks.NETHER_WART && iblockdata.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3 || iblockdata.isAir() && block1 == Blocks.SOUL_SAND; // Purpur
|
|
return block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata) || iblockdata.isAir() && block1 instanceof FarmBlock;
|
|
}
|
|
|
|
@@ -100,7 +103,7 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
Block block = iblockdata.getBlock();
|
|
Block block1 = world.getBlockState(this.aboveFarmlandPos.below()).getBlock();
|
|
|
|
- if (block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata)) {
|
|
+ if (block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata) && !this.clericWartFarmer || this.clericWartFarmer && block == Blocks.NETHER_WART && iblockdata.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3) { // Purpur
|
|
// CraftBukkit start
|
|
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, this.aboveFarmlandPos, Blocks.AIR.defaultBlockState()).isCancelled()) {
|
|
world.destroyBlock(this.aboveFarmlandPos, true, entity);
|
|
@@ -108,7 +111,7 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
// CraftBukkit end
|
|
}
|
|
|
|
- if (iblockdata.isAir() && block1 instanceof FarmBlock && entity.hasFarmSeeds()) {
|
|
+ if (iblockdata.isAir() && (block1 instanceof FarmBlock && !this.clericWartFarmer || this.clericWartFarmer && block1 == Blocks.SOUL_SAND) && entity.hasFarmSeeds()) { // Purpur
|
|
SimpleContainer inventorysubcontainer = entity.getInventory();
|
|
|
|
for (int j = 0; j < inventorysubcontainer.getContainerSize(); ++j) {
|
|
@@ -119,6 +122,12 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
BlockState iblockdata1;
|
|
|
|
// CraftBukkit start
|
|
+ // Purpur start
|
|
+ if (this.clericWartFarmer && itemstack.getItem() == Items.NETHER_WART) {
|
|
+ iblockdata1 = Blocks.NETHER_WART.defaultBlockState();
|
|
+ flag = true;
|
|
+ } else
|
|
+ // Purpur end
|
|
if (itemstack.is(Items.WHEAT_SEEDS)) {
|
|
iblockdata1 = Blocks.WHEAT.defaultBlockState();
|
|
flag = true;
|
|
@@ -145,7 +154,7 @@ public class HarvestFarmland extends Behavior<Villager> {
|
|
}
|
|
|
|
if (flag) {
|
|
- world.playSound((Player) null, (double) this.aboveFarmlandPos.getX(), (double) this.aboveFarmlandPos.getY(), (double) this.aboveFarmlandPos.getZ(), SoundEvents.CROP_PLANTED, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ world.playSound((Player) null, (double) this.aboveFarmlandPos.getX(), (double) this.aboveFarmlandPos.getY(), (double) this.aboveFarmlandPos.getZ(), this.clericWartFarmer ? SoundEvents.NETHER_WART_PLANTED : SoundEvents.CROP_PLANTED, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur
|
|
itemstack.shrink(1);
|
|
if (itemstack.isEmpty()) {
|
|
inventorysubcontainer.setItem(j, ItemStack.EMPTY);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java b/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java
|
|
index c3fb86dc3d94d3a0d2464f2dbb83cda2fb9f7bbe..fd77dd0c0bfaba57e5bdfd13f7a90241ecdf813a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java
|
|
@@ -57,7 +57,7 @@ public class InteractWithDoor {
|
|
|
|
if (iblockdata.is(BlockTags.WOODEN_DOORS, (blockbase_blockdata) -> {
|
|
return blockbase_blockdata.getBlock() instanceof DoorBlock;
|
|
- })) {
|
|
+ }) && !DoorBlock.requiresRedstone(entityliving.level, iblockdata, blockposition)) { // Purpur
|
|
DoorBlock blockdoor = (DoorBlock) iblockdata.getBlock();
|
|
|
|
if (!blockdoor.isOpen(iblockdata)) {
|
|
@@ -79,7 +79,7 @@ public class InteractWithDoor {
|
|
|
|
if (iblockdata1.is(BlockTags.WOODEN_DOORS, (blockbase_blockdata) -> {
|
|
return blockbase_blockdata.getBlock() instanceof DoorBlock;
|
|
- })) {
|
|
+ }) && !DoorBlock.requiresRedstone(entityliving.level, iblockdata, blockposition1)) { // Purpur
|
|
DoorBlock blockdoor1 = (DoorBlock) iblockdata1.getBlock();
|
|
|
|
if (!blockdoor1.isOpen(iblockdata1)) {
|
|
@@ -122,7 +122,7 @@ public class InteractWithDoor {
|
|
|
|
if (!iblockdata.is(BlockTags.WOODEN_DOORS, (blockbase_blockdata) -> {
|
|
return blockbase_blockdata.getBlock() instanceof DoorBlock;
|
|
- })) {
|
|
+ }) || DoorBlock.requiresRedstone(entity.level, iblockdata, blockposition)) { // Purpur
|
|
iterator.remove();
|
|
} else {
|
|
DoorBlock blockdoor = (DoorBlock) iblockdata.getBlock();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java
|
|
index 98373e013748817209b811d4adbb40a8787242a6..567b501f4de7556e55e2418d2f5700b4e4265235 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java
|
|
@@ -42,6 +42,7 @@ public class ShowTradesToPlayer extends Behavior<Villager> {
|
|
|
|
@Override
|
|
public boolean canStillUse(ServerLevel world, Villager entity, long time) {
|
|
+ if (!entity.level.purpurConfig.villagerDisplayTradeItem) return false; // Purpur
|
|
return this.checkExtraStartConditions(world, entity) && this.lookTime > 0 && entity.getBrain().getMemory(MemoryModuleType.INTERACTION_TARGET).isPresent();
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java
|
|
index 3af715e2f3f3949af614a8fcebbc4a835d48ca49..ade1e411ea1f3b4c9a417265e77b0d6861b222f9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java
|
|
@@ -56,6 +56,12 @@ public class TradeWithVillager extends Behavior<Villager> {
|
|
throwHalfStack(entity, ImmutableSet.of(Items.WHEAT), villager);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.villagerClericsFarmWarts && world.purpurConfig.villagerClericFarmersThrowWarts && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC && entity.getInventory().countItem(Items.NETHER_WART) > Items.NETHER_WART.getMaxStackSize() / 2) {
|
|
+ throwHalfStack(entity, ImmutableSet.of(Items.NETHER_WART), villager);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (!this.trades.isEmpty() && entity.getInventory().hasAnyOf(this.trades)) {
|
|
throwHalfStack(entity, this.trades, villager);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java
|
|
index cd7a90ec1073b2b452ca70decefe6a594445003b..47672e48c1cae73cffe532d622b296343fc12ef0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java
|
|
@@ -30,8 +30,13 @@ public class VillagerGoalPackages {
|
|
}
|
|
|
|
public static ImmutableList<Pair<Integer, ? extends BehaviorControl<? super Villager>>> getWorkPackage(VillagerProfession profession, float speed) {
|
|
+ // Purpur start
|
|
+ return getWorkPackage(profession, speed, false);
|
|
+ }
|
|
+ public static ImmutableList<Pair<Integer, ? extends BehaviorControl<? super Villager>>> getWorkPackage(VillagerProfession profession, float speed, boolean clericsFarmWarts) {
|
|
+ // Purpur end
|
|
WorkAtPoi workAtPoi;
|
|
- if (profession == VillagerProfession.FARMER) {
|
|
+ if (profession == VillagerProfession.FARMER || (clericsFarmWarts && profession == VillagerProfession.CLERIC)) { // Purpur
|
|
workAtPoi = new WorkAtComposter();
|
|
} else {
|
|
workAtPoi = new WorkAtPoi();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java
|
|
index 0951c04533e7c39b969d041271684355770b53c2..02d4ba2ccdce99ca97614baa7c8e49213126af96 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java
|
|
@@ -123,8 +123,10 @@ public class VillagerMakeLove extends Behavior<Villager> {
|
|
return Optional.empty();
|
|
}
|
|
// CraftBukkit end
|
|
- parent.setAge(6000);
|
|
- partner.setAge(6000);
|
|
+ // Purpur start
|
|
+ parent.setAge(world.purpurConfig.villagerBreedingTicks);
|
|
+ partner.setAge(world.purpurConfig.villagerBreedingTicks);
|
|
+ // Purpur end
|
|
world.addFreshEntityWithPassengers(entityvillager2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason
|
|
world.broadcastEntityEvent(entityvillager2, (byte) 12);
|
|
return Optional.of(entityvillager2);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
|
|
index 3d7586142aa5116964e4fffc6198f55fc6da324b..b4fb0af5bffffb9f0de3a2452c22b09fe92d7129 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
|
|
@@ -29,6 +29,20 @@ public class MoveControl implements Control {
|
|
this.mob = entity;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void setSpeedModifier(double speed) {
|
|
+ this.speedModifier = speed;
|
|
+ }
|
|
+
|
|
+ public void setForward(float forward) {
|
|
+ this.strafeForwards = forward;
|
|
+ }
|
|
+
|
|
+ public void setStrafe(float strafe) {
|
|
+ this.strafeRight = strafe;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public boolean hasWanted() {
|
|
return this.operation == MoveControl.Operation.MOVE_TO;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
|
|
index 7df56705a4a0de2dc4ff7ab133fc26612c219162..60d21d6171b9af20a4c6fcc0d564a31aaa4ecdba 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
|
|
@@ -3,7 +3,7 @@ package net.minecraft.world.entity.ai.control;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Mob;
|
|
|
|
-public class SmoothSwimmingLookControl extends LookControl {
|
|
+public class SmoothSwimmingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
private final int maxYRotFromCenter;
|
|
private static final int HEAD_TILT_X = 10;
|
|
private static final int HEAD_TILT_Y = 20;
|
|
@@ -14,7 +14,7 @@ public class SmoothSwimmingLookControl extends LookControl {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.lookAtCooldown > 0) {
|
|
--this.lookAtCooldown;
|
|
this.getYRotD().ifPresent((yaw) -> {
|
|
@@ -32,9 +32,9 @@ public class SmoothSwimmingLookControl extends LookControl {
|
|
}
|
|
|
|
float f = Mth.wrapDegrees(this.mob.yHeadRot - this.mob.yBodyRot);
|
|
- if (f < (float)(-this.maxYRotFromCenter)) {
|
|
+ if (f < (float) (-this.maxYRotFromCenter)) {
|
|
this.mob.yBodyRot -= 4.0F;
|
|
- } else if (f > (float)this.maxYRotFromCenter) {
|
|
+ } else if (f > (float) this.maxYRotFromCenter) {
|
|
this.mob.yBodyRot += 4.0F;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java
|
|
index 529435cf648d61f80a37f041cee3c6fc0b74ceb6..6c7195c93b5968845da35450e80022c70839487d 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java
|
|
@@ -32,7 +32,7 @@ public class BreakDoorGoal extends DoorInteractGoal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- return !super.canUse() ? false : (!this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.isValidDifficulty(this.mob.level.getDifficulty()) && !this.isOpen());
|
|
+ return !super.canUse() ? false : ((!this.mob.level.purpurConfig.zombieBypassMobGriefing && !this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) ? false : this.isValidDifficulty(this.mob.level.getDifficulty()) && !this.isOpen()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java
|
|
index 80aa539f7c6a6ee44338de084cdcdf5fb4ef996a..c55118a4d2237a33039b63dc797ccdb86b63344f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java
|
|
@@ -31,6 +31,12 @@ public class EatBlockGoal extends Goal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
+ // Purpur start
|
|
+ net.minecraft.world.level.chunk.LevelChunk chunk = this.mob.level.getChunkIfLoaded(this.mob.blockPosition());
|
|
+ if (chunk == null || chunk.playerChunk == null || !((net.minecraft.server.level.ServerLevel) this.mob.level).getChunkSource().chunkMap.anyPlayerCloseEnoughForSpawning(chunk.playerChunk, this.mob.chunkPosition(), false)) {
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
if (this.mob.getRandom().nextInt(this.mob.isBaby() ? 50 : 1000) != 0) {
|
|
return false;
|
|
} else {
|
|
@@ -69,7 +75,7 @@ public class EatBlockGoal extends Goal {
|
|
|
|
if (EatBlockGoal.IS_TALL_GRASS.test(this.level.getBlockState(blockposition))) {
|
|
// CraftBukkit
|
|
- if (!CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, Blocks.AIR.defaultBlockState(), !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)).isCancelled()) {
|
|
+ if (!CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, Blocks.AIR.defaultBlockState(), !this.level.purpurConfig.sheepBypassMobGriefing && !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)).isCancelled()) { // Purpur
|
|
this.level.destroyBlock(blockposition, false);
|
|
}
|
|
|
|
@@ -79,7 +85,7 @@ public class EatBlockGoal extends Goal {
|
|
|
|
if (this.level.getBlockState(blockposition1).is(Blocks.GRASS_BLOCK)) {
|
|
// CraftBukkit
|
|
- if (!CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)).isCancelled()) { // Paper - Fix wrong block state
|
|
+ if (!CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !this.level.purpurConfig.sheepBypassMobGriefing && !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)).isCancelled()) { // Paper - Fix wrong block state // Purpur
|
|
this.level.levelEvent(2001, blockposition1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState()));
|
|
this.level.setBlock(blockposition1, Blocks.DIRT.defaultBlockState(), 2);
|
|
}
|
|
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 1635818fc4b1788c0d397085239df6dd75b210ab..02978315bc2b828cc603ce7478408f3f82c249c2 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
|
|
@@ -105,8 +105,8 @@ public class GoalSelector {
|
|
}
|
|
|
|
public void tick() {
|
|
- ProfilerFiller profilerFiller = this.profiler.get();
|
|
- profilerFiller.push("goalCleanup");
|
|
+ //ProfilerFiller profilerFiller = this.profiler.get(); // Purpur
|
|
+ //profilerFiller.push("goalCleanup"); // Purpur
|
|
|
|
for(WrappedGoal wrappedGoal : this.availableGoals) {
|
|
if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) {
|
|
@@ -123,8 +123,8 @@ public class GoalSelector {
|
|
}
|
|
}
|
|
|
|
- profilerFiller.pop();
|
|
- profilerFiller.push("goalUpdate");
|
|
+ //profilerFiller.pop(); // Purpur
|
|
+ //profilerFiller.push("goalUpdate"); // Purpur
|
|
|
|
for(WrappedGoal wrappedGoal2 : this.availableGoals) {
|
|
// Paper start
|
|
@@ -144,13 +144,13 @@ public class GoalSelector {
|
|
}
|
|
}
|
|
|
|
- profilerFiller.pop();
|
|
+ //profilerFiller.pop(); // Purpur
|
|
this.tickRunningGoals(true);
|
|
}
|
|
|
|
public void tickRunningGoals(boolean tickAll) {
|
|
- ProfilerFiller profilerFiller = this.profiler.get();
|
|
- profilerFiller.push("goalTick");
|
|
+ //ProfilerFiller profilerFiller = this.profiler.get(); // Purpur
|
|
+ //profilerFiller.push("goalTick"); // Purpur
|
|
|
|
for(WrappedGoal wrappedGoal : this.availableGoals) {
|
|
if (wrappedGoal.isRunning() && (tickAll || wrappedGoal.requiresUpdateEveryTick())) {
|
|
@@ -158,7 +158,7 @@ public class GoalSelector {
|
|
}
|
|
}
|
|
|
|
- profilerFiller.pop();
|
|
+ //profilerFiller.pop(); // Purpur
|
|
}
|
|
|
|
public Set<WrappedGoal> getAvailableGoals() {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
|
index 721971f7618751a2e95f1c49fdc48a9c0c672cab..ad30f2d678cfc4b0d693e84e6e152c63b1b3cbb8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java
|
|
@@ -22,6 +22,7 @@ public class LlamaFollowCaravanGoal extends Goal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
+ if (!this.llama.level.purpurConfig.llamaJoinCaravans || !this.llama.shouldJoinCaravan) return false; // Purpur
|
|
if (!this.llama.isLeashed() && !this.llama.inCaravan()) {
|
|
List<Entity> list = this.llama.level.getEntities(this.llama, this.llama.getBoundingBox().inflate(9.0D, 4.0D, 9.0D), (entity) -> {
|
|
EntityType<?> entityType = entity.getType();
|
|
@@ -71,6 +72,7 @@ public class LlamaFollowCaravanGoal extends Goal {
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
+ if (!this.llama.shouldJoinCaravan) return false; // Purpur
|
|
if (this.llama.inCaravan() && this.llama.getCaravanHead().isAlive() && this.firstIsLeashed(this.llama, 0)) {
|
|
double d = this.llama.distanceToSqr(this.llama.getCaravanHead());
|
|
if (d > 676.0D) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java
|
|
index 6558b0d4bea99948fdc2b51751f3cfdc239d4b67..d85dabebbbbe213e791b8a3be3c6df05b959e40c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java
|
|
@@ -111,9 +111,9 @@ public class RangedBowAttackGoal<T extends Monster & RangedAttackMob> extends Go
|
|
|
|
this.mob.getMoveControl().strafe(this.strafingBackwards ? -0.5F : 0.5F, this.strafingClockwise ? 0.5F : -0.5F);
|
|
this.mob.lookAt(livingEntity, 30.0F, 30.0F);
|
|
- } else {
|
|
+ } //else { // Purpur - fix MC-121706
|
|
this.mob.getLookControl().setLookAt(livingEntity, 30.0F, 30.0F);
|
|
- }
|
|
+ //} // Purpur
|
|
|
|
if (this.mob.isUsingItem()) {
|
|
if (!bl && this.seeTime < -60) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
|
|
index d3e91faee8805e88d850740fb5de9e5c8288c48b..fe526ebf395ff9813b94284fc3f0142323d6a303 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
|
|
@@ -40,7 +40,7 @@ public class RemoveBlockGoal extends MoveToBlockGoal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- if (!this.removerMob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!this.removerMob.level.purpurConfig.zombieBypassMobGriefing && !this.removerMob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
} else if (this.nextStartTick > 0) {
|
|
--this.nextStartTick;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
|
index 5c64905e90ccca6e0b347241ddf9cc3f71058b8e..3bd7521b131b2b40f807bdc7ab95e64cf9bcdadc 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java
|
|
@@ -63,7 +63,7 @@ public class RunAroundLikeCrazyGoal extends Goal {
|
|
int j = this.horse.getMaxTemper();
|
|
|
|
// CraftBukkit - fire EntityTameEvent
|
|
- if (j > 0 && this.horse.getRandom().nextInt(j) < i && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) {
|
|
+ if ((this.horse.level.purpurConfig.alwaysTameInCreative && ((Player) entity).getAbilities().instabuild) || (j > 0 && this.horse.getRandom().nextInt(j) < i && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled())) { // Purpur
|
|
this.horse.tameWithName((Player) entity);
|
|
return;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java
|
|
index e241ae250f4f04a17ef2c583d00b065a4ca56a4c..02b567e4e808e1a809d285ef39e1abc54e1e6ad2 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java
|
|
@@ -54,6 +54,14 @@ public class SwellGoal extends Goal {
|
|
this.creeper.setSwellDir(-1);
|
|
} else {
|
|
this.creeper.setSwellDir(1);
|
|
+ // Purpur start
|
|
+ if (this.creeper.getLevel().purpurConfig.creeperEncircleTarget) {
|
|
+ net.minecraft.world.phys.Vec3 relative = this.creeper.position().subtract(this.target.position());
|
|
+ relative = relative.yRot((float) Math.PI / 3).normalize().multiply(2, 2, 2);
|
|
+ net.minecraft.world.phys.Vec3 destination = this.target.position().add(relative);
|
|
+ this.creeper.getNavigation().moveTo(destination.x, destination.y, destination.z, 1);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java
|
|
index 79bb13c5614bab1f0749c5f8f57f762c6216c564..2cbc9adc8e417def48be03d08174a5833068ec65 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java
|
|
@@ -62,7 +62,7 @@ public class TemptGoal extends Goal {
|
|
}
|
|
|
|
private boolean shouldFollow(LivingEntity entity) {
|
|
- return this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem());
|
|
+ return (this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem())) && (!(this.mob instanceof net.minecraft.world.entity.npc.Villager villager) || !villager.isSleeping()); // Purpur Fix #512
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
|
index 7ffe5bef3778d5971ea4ceadf3102725fd0d08cd..6d127ed3da899851ca95b2be6792e2abca1aca12 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
|
@@ -172,12 +172,12 @@ public abstract class PathNavigation {
|
|
}
|
|
}
|
|
// Paper end
|
|
- this.level.getProfiler().push("pathfind");
|
|
+ //this.level.getProfiler().push("pathfind"); // Purpur
|
|
BlockPos blockPos = useHeadPos ? this.mob.blockPosition().above() : this.mob.blockPosition();
|
|
int i = (int)(followRange + (float)range);
|
|
PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i));
|
|
Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, positions, followRange, distance, this.maxVisitedNodesMultiplier);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
if (path != null && path.getTarget() != null) {
|
|
this.targetPos = path.getTarget();
|
|
this.reachRange = distance;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
|
|
index cb1d91f9fe98f21c2afbe3894dfd9bca3bdd3ba6..d2703432af207c74ea8d298a784329c3219d2f13 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java
|
|
@@ -22,6 +22,13 @@ public class SecondaryPoiSensor extends Sensor<Villager> {
|
|
|
|
@Override
|
|
protected void doTick(ServerLevel world, Villager entity) {
|
|
+ // Purpur start - make sure clerics don't wander to soul sand when the option is off
|
|
+ Brain<?> brain = entity.getBrain();
|
|
+ if (!world.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().getProfession() == net.minecraft.world.entity.npc.VillagerProfession.CLERIC) {
|
|
+ brain.eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
ResourceKey<Level> resourceKey = world.dimension();
|
|
BlockPos blockPos = entity.blockPosition();
|
|
List<GlobalPos> list = Lists.newArrayList();
|
|
@@ -38,7 +45,7 @@ public class SecondaryPoiSensor extends Sensor<Villager> {
|
|
}
|
|
}
|
|
|
|
- Brain<?> brain = entity.getBrain();
|
|
+ //Brain<?> brain = entity.getBrain(); // Purpur - moved up
|
|
if (!list.isEmpty()) {
|
|
brain.setMemory(MemoryModuleType.SECONDARY_JOB_SITE, list);
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java
|
|
index 288c6627906d07c0d223eacd84ae4eb31a349998..9babe636176da3c40598eb5bdac0919a1704eaa0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java
|
|
@@ -26,9 +26,9 @@ public class Sensing {
|
|
} else if (this.unseen.contains(i)) {
|
|
return false;
|
|
} else {
|
|
- this.mob.level.getProfiler().push("hasLineOfSight");
|
|
+ //this.mob.level.getProfiler().push("hasLineOfSight"); // Purpur
|
|
boolean bl = this.mob.hasLineOfSight(entity);
|
|
- this.mob.level.getProfiler().pop();
|
|
+ //this.mob.level.getProfiler().pop(); // Purpur
|
|
if (bl) {
|
|
this.seen.add(i);
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
|
|
index fcdb9bde8e1605e30dde3e580491522d4b62cdc0..7094701d213c73ba47ace806962244c10fdf4dda 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
|
|
@@ -46,10 +46,10 @@ public abstract class Sensor<E extends LivingEntity> {
|
|
if (--this.timeToTick <= 0L) {
|
|
// Paper start - configurable sensor tick rate and timings
|
|
this.timeToTick = java.util.Objects.requireNonNullElse(world.paperConfig().tickRates.sensor.get(entity.getType(), this.configKey), this.scanRate);
|
|
- this.timing.startTiming();
|
|
+ //this.timing.startTiming(); // Purpur
|
|
// Paper end
|
|
this.doTick(world, entity);
|
|
- this.timing.stopTiming(); // Paper - sensor timings
|
|
+ //this.timing.stopTiming(); // Paper - sensor timings // Purpur
|
|
}
|
|
|
|
}
|
|
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 e752c83df50fb9b670ecea2abc95426c2a009b6f..baa4f9026d31de92210300ecb8ee8c1b6d575435 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
|
|
@@ -64,6 +64,10 @@ public class TargetingConditions {
|
|
return false;
|
|
} else if (this.selector != null && !this.selector.test(targetEntity)) {
|
|
return false;
|
|
+ // Purpur start
|
|
+ } else if (!targetEntity.level.purpurConfig.idleTimeoutTargetPlayer && targetEntity instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) {
|
|
+ return false;
|
|
+ // Purpur end
|
|
} else {
|
|
if (baseEntity == null) {
|
|
if (this.isCombat && (!targetEntity.canBeSeenAsEnemy() || targetEntity.level.getDifficulty() == Difficulty.PEACEFUL)) {
|
|
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 59338d739b6130f667d151bc27646c4a346886a2..bfa1b84cba0dface99fcd3cf573be49f6f1f7d55 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
@@ -18,6 +18,7 @@ import net.minecraft.world.entity.EntityDimensions;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.MobSpawnType;
|
|
+import net.minecraft.world.entity.MoverType;
|
|
import net.minecraft.world.entity.Pose;
|
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
@@ -41,12 +42,81 @@ public class Bat extends AmbientCreature {
|
|
|
|
public Bat(EntityType<? extends Bat> type, Level world) {
|
|
super(type, world);
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.075F); // Purpur
|
|
if (!world.isClientSide) {
|
|
this.setResting(true);
|
|
}
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean shouldSendAttribute(net.minecraft.world.entity.ai.attributes.Attribute attribute) { return attribute != Attributes.FLYING_SPEED; } // Fixes log spam on clients
|
|
+
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.batRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.batRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.batControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.batMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ if (isResting()) {
|
|
+ setResting(false);
|
|
+ level.levelEvent(null, 1025, new BlockPos(this).above(), 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(MoverType.SELF, mot.multiply(speed, 0.25, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.batMaxHealth);
|
|
+ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level.purpurConfig.batFollowRange);
|
|
+ this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(this.level.purpurConfig.batKnockbackResistance);
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level.purpurConfig.batMovementSpeed);
|
|
+ this.getAttribute(Attributes.FLYING_SPEED).setBaseValue(this.level.purpurConfig.batFlyingSpeed);
|
|
+ this.getAttribute(Attributes.ARMOR).setBaseValue(this.level.purpurConfig.batArmor);
|
|
+ this.getAttribute(Attributes.ARMOR_TOUGHNESS).setBaseValue(this.level.purpurConfig.batArmorToughness);
|
|
+ this.getAttribute(Attributes.ATTACK_KNOCKBACK).setBaseValue(this.level.purpurConfig.batAttackKnockback);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.batTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.batAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean isFlapping() {
|
|
return !this.isResting() && this.tickCount % Bat.TICKS_PER_FLAP == 0;
|
|
@@ -96,7 +166,7 @@ public class Bat extends AmbientCreature {
|
|
protected void pushEntities() {}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D);
|
|
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
|
|
}
|
|
|
|
public boolean isResting() {
|
|
@@ -128,6 +198,14 @@ public class Bat extends AmbientCreature {
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
super.customServerAiStep();
|
|
BlockPos blockposition = this.blockPosition();
|
|
BlockPos blockposition1 = blockposition.above();
|
|
@@ -241,7 +319,7 @@ public class Bat extends AmbientCreature {
|
|
int i = world.getMaxLocalRawBrightness(pos);
|
|
byte b0 = 4;
|
|
|
|
- if (Bat.isHalloween()) {
|
|
+ if (Bat.isHalloweenSeason(world.getMinecraftWorld())) { // Purpur
|
|
b0 = 7;
|
|
} else if (random.nextBoolean()) {
|
|
return false;
|
|
@@ -255,6 +333,7 @@ public class Bat extends AmbientCreature {
|
|
private static boolean isSpookySeason = false;
|
|
private static final int ONE_HOUR = 20 * 60 * 60;
|
|
private static int lastSpookyCheck = -ONE_HOUR;
|
|
+ public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur
|
|
private static boolean isHalloween() {
|
|
if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) {
|
|
LocalDate localdate = LocalDate.now();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
|
|
index 1f85f34c1e50f34fb270d2fac7d307c82a550bfa..324f52edd95b5f9a498e46def8c14435cfd00abb 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
|
|
@@ -94,7 +94,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
- this.goalSelector.addGoal(0, new PanicGoal(this, 1.25D));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6D, 1.4D, EntitySelector.NO_SPECTATORS::test));
|
|
this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this));
|
|
}
|
|
@@ -107,7 +107,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
@Override
|
|
public void travel(Vec3 movementInput) {
|
|
if (this.isEffectiveAi() && this.isInWater()) {
|
|
- this.moveRelative(0.01F, movementInput);
|
|
+ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, movementInput); // Purpur
|
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
|
this.setDeltaMovement(this.getDeltaMovement().scale(0.9D));
|
|
if (this.getTarget() == null) {
|
|
@@ -166,7 +166,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
protected void playStepSound(BlockPos pos, BlockState state) {
|
|
}
|
|
|
|
- static class FishMoveControl extends MoveControl {
|
|
+ static class FishMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur
|
|
private final AbstractFish fish;
|
|
|
|
FishMoveControl(AbstractFish owner) {
|
|
@@ -174,14 +174,22 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
|
|
this.fish = owner;
|
|
}
|
|
|
|
+ // Purpur start
|
|
@Override
|
|
- public void tick() {
|
|
+ public void purpurTick(Player rider) {
|
|
+ super.purpurTick(rider);
|
|
+ fish.setDeltaMovement(fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.fish.isEyeInFluid(FluidTags.WATER)) {
|
|
this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
|
|
}
|
|
|
|
if (this.operation == MoveControl.Operation.MOVE_TO && !this.fish.getNavigation().isDone()) {
|
|
- float f = (float)(this.speedModifier * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float f = (float)(this.getSpeedModifier() * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
|
|
this.fish.setSpeed(Mth.lerp(0.125F, this.fish.getSpeed(), f));
|
|
double d = this.wantedX - this.fish.getX();
|
|
double e = this.wantedY - this.fish.getY();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Animal.java b/src/main/java/net/minecraft/world/entity/animal/Animal.java
|
|
index 3c4d142e982c34a23bdb5da1f51c8dcacc0532c1..2ac88f06ebb79e515cd9934ac1e3e2c8003d9e3c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Animal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java
|
|
@@ -39,6 +39,7 @@ public abstract class Animal extends AgeableMob {
|
|
@Nullable
|
|
public UUID loveCause;
|
|
public ItemStack breedItem; // CraftBukkit - Add breedItem variable
|
|
+ public abstract int getPurpurBreedTime(); // Purpur
|
|
|
|
protected Animal(EntityType<? extends Animal> type, Level world) {
|
|
super(type, world);
|
|
@@ -150,7 +151,7 @@ public abstract class Animal extends AgeableMob {
|
|
if (this.isFood(itemstack)) {
|
|
int i = this.getAge();
|
|
|
|
- if (!this.level.isClientSide && i == 0 && this.canFallInLove()) {
|
|
+ if (!this.level.isClientSide && i == 0 && this.canFallInLove() && (this.level.purpurConfig.animalBreedingCooldownSeconds <= 0 || !this.level.hasBreedingCooldown(player.getUUID(), this.getClass()))) { // Purpur
|
|
this.usePlayerItem(player, hand, itemstack);
|
|
this.setInLove(player);
|
|
return InteractionResult.SUCCESS;
|
|
@@ -237,6 +238,14 @@ public abstract class Animal extends AgeableMob {
|
|
if (entityplayer == null && other.getLoveCause() != null) {
|
|
entityplayer = other.getLoveCause();
|
|
}
|
|
+ // Purpur start
|
|
+ if (entityplayer != null && world.purpurConfig.animalBreedingCooldownSeconds > 0) {
|
|
+ if (world.hasBreedingCooldown(entityplayer.getUUID(), this.getClass())) {
|
|
+ return;
|
|
+ }
|
|
+ world.addBreedingCooldown(entityplayer.getUUID(), this.getClass());
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start - call EntityBreedEvent
|
|
entityageable.setBaby(true);
|
|
entityageable.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F);
|
|
@@ -253,8 +262,10 @@ public abstract class Animal extends AgeableMob {
|
|
CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer, this, other, entityageable);
|
|
}
|
|
|
|
- this.setAge(6000);
|
|
- other.setAge(6000);
|
|
+ // Purpur start
|
|
+ this.setAge(this.getPurpurBreedTime());
|
|
+ other.setAge(other.getPurpurBreedTime());
|
|
+ // Purpur end
|
|
this.resetLove();
|
|
other.resetLove();
|
|
world.addFreshEntityWithPassengers(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java
|
|
index c33e5c51839c8e6ec04c1b302127d2bf0f48664c..d47dc0c3fe8c2b80d7b7eb828a12af6eb32145e4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
|
|
@@ -43,6 +43,7 @@ import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.MobType;
|
|
+import net.minecraft.world.entity.MoverType;
|
|
import net.minecraft.world.entity.NeutralMob;
|
|
import net.minecraft.world.entity.PathfinderMob;
|
|
import net.minecraft.world.entity.Pose;
|
|
@@ -143,6 +144,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
public Bee(EntityType<? extends Bee> type, Level world) {
|
|
super(type, world);
|
|
this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
|
|
+ final org.purpurmc.purpur.controller.FlyingMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.25F, 1.0F, false); // Purpur
|
|
// Paper start - apply gravity to bees when they get stuck in the void, fixes MC-167279
|
|
class BeeFlyingMoveControl extends FlyingMoveControl {
|
|
public BeeFlyingMoveControl(final Mob entity, final int maxPitchChange, final boolean noGravity) {
|
|
@@ -151,22 +153,89 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ flyingController.purpurTick(mob.getRider());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
if (this.mob.getY() <= Bee.this.level.getMinBuildHeight()) {
|
|
this.mob.setNoGravity(false);
|
|
}
|
|
super.tick();
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean hasWanted() {
|
|
+ return mob.getRider() != null || !mob.isControllable() || super.hasWanted();
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
this.moveControl = new BeeFlyingMoveControl(this, 20, true);
|
|
// Paper end
|
|
this.lookControl = new Bee.BeeLookControl(this);
|
|
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F);
|
|
- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur
|
|
this.setPathfindingMalus(BlockPathTypes.WATER_BORDER, 16.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.FENCE, -1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.beeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.beeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.beeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.beeMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(MoverType.SELF, mot.multiply(speed, speed, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.beeMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.beeBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.beeTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.beeAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
@@ -181,6 +250,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.399999976158142D, true));
|
|
this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal());
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
@@ -196,6 +266,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.goalSelector.addGoal(7, new Bee.BeeGrowCropGoal());
|
|
this.goalSelector.addGoal(8, new Bee.BeeWanderGoal());
|
|
this.goalSelector.addGoal(9, new FloatGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new Bee.BeeHurtByOtherGoal(this)).setAlertOthers(new Class[0]));
|
|
this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this));
|
|
this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true));
|
|
@@ -344,7 +415,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
|
|
boolean wantsToEnterHive() {
|
|
if (this.stayOutOfHiveCountdown <= 0 && !this.beePollinateGoal.isPollinating() && !this.hasStung() && this.getTarget() == null) {
|
|
- boolean flag = this.isTiredOfLookingForNectar() || this.level.isRaining() || this.level.isNight() || this.hasNectar();
|
|
+ boolean flag = this.isTiredOfLookingForNectar() || (this.level.isRaining() && !this.level.purpurConfig.beeCanWorkInRain) || (this.level.isNight() && !this.level.purpurConfig.beeCanWorkAtNight) || this.hasNectar(); // Purpur
|
|
|
|
return flag && !this.isHiveNearFire();
|
|
} else {
|
|
@@ -384,6 +455,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.hurt(this.damageSources().drown(), 1.0F);
|
|
}
|
|
|
|
+ if (flag && !this.level.purpurConfig.beeDiesAfterSting) setHasStung(false); else // Purpur
|
|
if (flag) {
|
|
++this.timeSinceSting;
|
|
if (this.timeSinceSting % 5 == 0 && this.random.nextInt(Mth.clamp(1200 - this.timeSinceSting, 1, 1200)) == 0) {
|
|
@@ -732,6 +804,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
if (optional.isPresent()) {
|
|
Bee.this.savedFlowerPos = (BlockPos) optional.get();
|
|
Bee.this.navigation.moveTo((double) Bee.this.savedFlowerPos.getX() + 0.5D, (double) Bee.this.savedFlowerPos.getY() + 0.5D, (double) Bee.this.savedFlowerPos.getZ() + 0.5D, 1.2000000476837158D);
|
|
+ new org.purpurmc.purpur.event.entity.BeeFoundFlowerEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(Bee.this.level, Bee.this.savedFlowerPos)).callEvent(); // Purpur
|
|
return true;
|
|
} else {
|
|
Bee.this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(Bee.this.random, 20, 60);
|
|
@@ -788,6 +861,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.pollinating = false;
|
|
Bee.this.navigation.stop();
|
|
Bee.this.remainingCooldownBeforeLocatingNewFlower = 200;
|
|
+ new org.purpurmc.purpur.event.entity.BeeStopPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), Bee.this.savedFlowerPos == null ? null : io.papermc.paper.util.MCUtil.toLocation(Bee.this.level, Bee.this.savedFlowerPos), Bee.this.hasNectar()).callEvent(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -834,6 +908,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
this.setWantedPos();
|
|
}
|
|
|
|
+ if (this.successfulPollinatingTicks == 0) new org.purpurmc.purpur.event.entity.BeeStartedPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(Bee.this.level, Bee.this.savedFlowerPos)).callEvent(); // Purpur
|
|
++this.successfulPollinatingTicks;
|
|
if (Bee.this.random.nextFloat() < 0.05F && this.successfulPollinatingTicks > this.lastSoundPlayedTick + 60) {
|
|
this.lastSoundPlayedTick = this.successfulPollinatingTicks;
|
|
@@ -878,16 +953,16 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
|
}
|
|
}
|
|
|
|
- private class BeeLookControl extends LookControl {
|
|
+ private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
BeeLookControl(Mob entity) {
|
|
super(entity);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (!Bee.this.isAngry()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java
|
|
index 72b30a5cdeb8a43702d9ab5f198311929761fad1..fe08d83a49efe5e1648cafc50e9184dbd0db2115 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Cat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java
|
|
@@ -97,6 +97,51 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.catRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.catRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.catControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setInSittingPose(false);
|
|
+ setLying(false);
|
|
+ setRelaxStateOne(false);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.catMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.catBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.catTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.catAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public ResourceLocation getResourceLocation() {
|
|
return this.getVariant().texture();
|
|
}
|
|
@@ -105,6 +150,7 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
|
|
protected void registerGoals() {
|
|
this.temptGoal = new Cat.CatTemptGoal(this, 0.6D, Cat.TEMPT_INGREDIENT, true);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.5D));
|
|
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
|
this.goalSelector.addGoal(3, new Cat.CatRelaxOnOwnerGoal(this));
|
|
@@ -117,6 +163,7 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
|
|
this.goalSelector.addGoal(10, new BreedGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(11, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F));
|
|
this.goalSelector.addGoal(12, new LookAtPlayerGoal(this, Player.class, 10.0F));
|
|
+ this.targetSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Rabbit.class, false, (Predicate) null));
|
|
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
@@ -308,6 +355,14 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
|
|
return Mth.lerp(tickDelta, this.relaxStateOneAmountO, this.relaxStateOneAmount);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void tame(Player player) {
|
|
+ setCollarColor(level.purpurConfig.catDefaultCollarColor);
|
|
+ super.tame(player);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public Cat getBreedOffspring(ServerLevel world, AgeableMob entity) {
|
|
@@ -373,6 +428,7 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
|
|
|
|
@Override
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ if (getRider() != null) return InteractionResult.PASS; // Purpur
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
Item item = itemstack.getItem();
|
|
|
|
@@ -419,7 +475,7 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
|
|
}
|
|
} else if (this.isFood(itemstack)) {
|
|
this.usePlayerItem(player, hand, itemstack);
|
|
- if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit
|
|
+ if ((this.level.purpurConfig.alwaysTameInCreative && player.getAbilities().instabuild) || (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled())) { // CraftBukkit // Purpur
|
|
this.tame(player);
|
|
this.setOrderedToSit(true);
|
|
this.level.broadcastEntityEvent(this, (byte) 7);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
|
|
index b4dc621cb1be7cb4bf1cb31f921d4e9f6cffef88..c5e81244331d76535028f8296d10939933010d09 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
|
|
@@ -54,16 +54,65 @@ public class Chicken extends Animal {
|
|
this.setPathfindingMalus(BlockPathTypes.WATER, 0.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.chickenRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.chickenRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.chickenControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.chickenMaxHealth);
|
|
+ if (level.purpurConfig.chickenRetaliate) {
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(2.0D);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.chickenBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.chickenTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.chickenAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
- this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ // this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); // Purpur - moved down
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new TemptGoal(this, 1.0D, Chicken.FOOD_ITEMS, false));
|
|
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.1D));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.chickenRetaliate) {
|
|
+ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.0D, false));
|
|
+ this.targetSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal(this));
|
|
+ } else {
|
|
+ this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D));
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -72,7 +121,7 @@ public class Chicken extends Animal {
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D);
|
|
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java
|
|
index 824e5e4fe7619ae46061c3c978c9a044db8c84ab..2a45b487e5305e7c40cc8de4ddbb142af4b041de 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Cod.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java
|
|
@@ -13,6 +13,33 @@ public class Cod extends AbstractSchoolingFish {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.codRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.codControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.codMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.codTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.codAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public ItemStack getBucketItemStack() {
|
|
return new ItemStack(Items.COD_BUCKET);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java
|
|
index abae850f5babfd75c7547e88fb7637e9775991d3..54d9213d9de26a14a5ca770440d098bf0438373e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Cow.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java
|
|
@@ -2,6 +2,7 @@ package net.minecraft.world.entity.animal;
|
|
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
@@ -29,6 +30,7 @@ import net.minecraft.world.item.ItemUtils;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.item.crafting.Ingredient;
|
|
import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
// CraftBukkit start
|
|
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
|
@@ -36,25 +38,74 @@ import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
// CraftBukkit end
|
|
|
|
public class Cow extends Animal {
|
|
+ private boolean isNaturallyAggressiveToPlayers; // Purpur
|
|
|
|
public Cow(EntityType<? extends Cow> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.cowRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.cowRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.cowControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.cowMaxHealth);
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level.purpurConfig.cowNaturallyAggressiveToPlayersDamage); // Purpur
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.cowBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.cowTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.MobSpawnType spawnReason, net.minecraft.world.entity.SpawnGroupData entityData, net.minecraft.nbt.CompoundTag entityNbt) {
|
|
+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance;
|
|
+ return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.cowAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D));
|
|
+ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
+ if (level.purpurConfig.cowFeedMushrooms > 0) this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT, Blocks.RED_MUSHROOM.asItem(), Blocks.BROWN_MUSHROOM.asItem()), false)); else // Purpur
|
|
this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT), false));
|
|
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, target -> isNaturallyAggressiveToPlayers)); // Purpur
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D);
|
|
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -84,6 +135,7 @@ public class Cow extends Animal {
|
|
|
|
@Override
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ if (getRider() != null) return InteractionResult.PASS; // Purpur
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (itemstack.is(Items.BUCKET) && !this.isBaby()) {
|
|
@@ -91,7 +143,7 @@ public class Cow extends Animal {
|
|
org.bukkit.event.player.PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) player.level, player, this.blockPosition(), this.blockPosition(), null, itemstack, Items.MILK_BUCKET, hand);
|
|
|
|
if (event.isCancelled()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
|
|
@@ -100,6 +152,10 @@ public class Cow extends Animal {
|
|
|
|
player.setItemInHand(hand, itemstack1);
|
|
return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
+ // Purpur start - feed mushroom to change to mooshroom
|
|
+ } else if (level.purpurConfig.cowFeedMushrooms > 0 && this.getType() != EntityType.MOOSHROOM && isMushroom(itemstack)) {
|
|
+ return this.feedMushroom(player, itemstack);
|
|
+ // Purpur end
|
|
} else {
|
|
return super.mobInteract(player, hand);
|
|
}
|
|
@@ -115,4 +171,69 @@ public class Cow extends Animal {
|
|
protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
|
|
return this.isBaby() ? dimensions.height * 0.95F : 1.3F;
|
|
}
|
|
+
|
|
+ // Purpur start - feed mushroom to change to mooshroom
|
|
+ private int redMushroomsFed = 0;
|
|
+ private int brownMushroomsFed = 0;
|
|
+
|
|
+ private boolean isMushroom(ItemStack stack) {
|
|
+ return stack.getItem() == Blocks.RED_MUSHROOM.asItem() || stack.getItem() == Blocks.BROWN_MUSHROOM.asItem();
|
|
+ }
|
|
+
|
|
+ private int incrementFeedCount(ItemStack stack) {
|
|
+ if (stack.getItem() == Blocks.RED_MUSHROOM.asItem()) {
|
|
+ return ++redMushroomsFed;
|
|
+ } else {
|
|
+ return ++brownMushroomsFed;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private InteractionResult feedMushroom(Player player, ItemStack stack) {
|
|
+ level.broadcastEntityEvent(this, (byte) 18); // hearts
|
|
+ playSound(SoundEvents.COW_MILK, 1.0F, 1.0F);
|
|
+ if (incrementFeedCount(stack) < level.purpurConfig.cowFeedMushrooms) {
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.CONSUME; // require 5 mushrooms to transform (prevents mushroom duping)
|
|
+ }
|
|
+ MushroomCow mooshroom = EntityType.MOOSHROOM.create(level);
|
|
+ if (mooshroom == null) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (stack.getItem() == Blocks.BROWN_MUSHROOM.asItem()) {
|
|
+ mooshroom.setVariant(MushroomCow.MushroomType.BROWN);
|
|
+ } else {
|
|
+ mooshroom.setVariant(MushroomCow.MushroomType.RED);
|
|
+ }
|
|
+ mooshroom.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
|
+ mooshroom.setHealth(this.getHealth());
|
|
+ mooshroom.setAge(getAge());
|
|
+ mooshroom.copyPosition(this);
|
|
+ mooshroom.setYBodyRot(this.yBodyRot);
|
|
+ mooshroom.setYHeadRot(this.getYHeadRot());
|
|
+ mooshroom.yRotO = this.yRotO;
|
|
+ mooshroom.xRotO = this.xRotO;
|
|
+ if (this.hasCustomName()) {
|
|
+ mooshroom.setCustomName(this.getCustomName());
|
|
+ }
|
|
+ if (CraftEventFactory.callEntityTransformEvent(this, mooshroom, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), mooshroom.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.INFECTED).callEvent()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ this.level.addFreshEntity(mooshroom);
|
|
+ this.remove(RemovalReason.DISCARDED);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ for (int i = 0; i < 15; ++i) {
|
|
+ ((ServerLevel) level).sendParticles(((ServerLevel) level).players(), null, ParticleTypes.HAPPY_VILLAGER,
|
|
+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1,
|
|
+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
|
|
index e93abb02744b5cd8db88e01b6ccf145498903b11..a077edbe97ce89e11a26fe3ebeb0bdd996593f78 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
|
|
@@ -78,19 +78,104 @@ public class Dolphin extends WaterAnimal {
|
|
public static final Predicate<ItemEntity> ALLOWED_ITEMS = (entityitem) -> {
|
|
return !entityitem.hasPickUpDelay() && entityitem.isAlive() && entityitem.isInWater();
|
|
};
|
|
+ private int spitCooldown; // Purpur
|
|
+ private boolean isNaturallyAggressiveToPlayers; // Purpur
|
|
|
|
public Dolphin(EntityType<? extends Dolphin> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur start
|
|
+ class DolphinMoveControl extends SmoothSwimmingMoveControl {
|
|
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterMoveControllerWASD;
|
|
+ private final Dolphin dolphin;
|
|
+
|
|
+ public DolphinMoveControl(Dolphin dolphin, int pitchChange, int yawChange, float speedInWater, float speedInAir, boolean buoyant) {
|
|
+ super(dolphin, pitchChange, yawChange, speedInWater, speedInAir, buoyant);
|
|
+ this.dolphin = dolphin;
|
|
+ this.waterMoveControllerWASD = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(dolphin);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (dolphin.getRider() != null && dolphin.isControllable()) {
|
|
+ purpurTick(dolphin.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void purpurTick(Player rider) {
|
|
+ if (dolphin.getAirSupply() < 150) {
|
|
+ // if drowning override player WASD controls to find air
|
|
+ super.tick();
|
|
+ } else {
|
|
+ waterMoveControllerWASD.purpurTick(rider);
|
|
+ dolphin.setDeltaMovement(dolphin.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ this.moveControl = new DolphinMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur end
|
|
this.lookControl = new SmoothSwimmingLookControl(this, 10);
|
|
this.setCanPickUpLoot(true);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.dolphinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.dolphinControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (spitCooldown == 0 && getRider() != null) {
|
|
+ spitCooldown = level.purpurConfig.dolphinSpitCooldown;
|
|
+
|
|
+ org.bukkit.craftbukkit.entity.CraftPlayer player = (org.bukkit.craftbukkit.entity.CraftPlayer) getRider().getBukkitEntity();
|
|
+ if (!player.hasPermission("allow.special.dolphin")) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ org.bukkit.Location loc = player.getEyeLocation();
|
|
+ loc.setPitch(loc.getPitch() - 10);
|
|
+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(10).add(loc.toVector());
|
|
+
|
|
+ org.purpurmc.purpur.entity.DolphinSpit spit = new org.purpurmc.purpur.entity.DolphinSpit(level, this);
|
|
+ spit.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), level.purpurConfig.dolphinSpitSpeed, 5.0F);
|
|
+
|
|
+ level.addFreshEntity(spit);
|
|
+ playSound(SoundEvents.DOLPHIN_ATTACK, 1.0F, 1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.dolphinMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.dolphinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.dolphinAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) {
|
|
this.setAirSupply(this.getMaxAirSupply());
|
|
this.setXRot(0.0F);
|
|
+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance; // Purpur
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt);
|
|
}
|
|
|
|
@@ -160,17 +245,21 @@ public class Dolphin extends WaterAnimal {
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new BreathAirGoal(this));
|
|
this.goalSelector.addGoal(0, new TryFindWaterGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur
|
|
this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this));
|
|
this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0D));
|
|
this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10));
|
|
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
|
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(5, new DolphinJumpGoal(this, 10));
|
|
- this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2000000476837158D, true));
|
|
+ //this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - moved up
|
|
this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal());
|
|
this.goalSelector.addGoal(8, new FollowBoatGoal(this));
|
|
this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0D, 1.0D));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Guardian.class})).setAlertOthers());
|
|
+ this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, target -> isNaturallyAggressiveToPlayers)); // Purpur
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
@@ -221,7 +310,7 @@ public class Dolphin extends WaterAnimal {
|
|
|
|
@Override
|
|
protected boolean canRide(Entity entity) {
|
|
- return true;
|
|
+ return boardingCooldown <= 0; // Purpur - make dolphin honor ride cooldown like all other non-boss mobs;
|
|
}
|
|
|
|
@Override
|
|
@@ -256,6 +345,11 @@ public class Dolphin extends WaterAnimal {
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
+ // Purpur start
|
|
+ if (spitCooldown > 0) {
|
|
+ spitCooldown--;
|
|
+ }
|
|
+ // Purpur end
|
|
if (this.isNoAi()) {
|
|
this.setAirSupply(this.getMaxAirSupply());
|
|
} else {
|
|
@@ -401,6 +495,7 @@ public class Dolphin extends WaterAnimal {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
+ if (this.dolphin.level.purpurConfig.dolphinDisableTreasureSearching) return false; // Purpur
|
|
return this.dolphin.gotFish() && this.dolphin.getAirSupply() >= 100 && this.dolphin.level.getWorld().canGenerateStructures(); // MC-151364, SPIGOT-5494: hangs if generate-structures=false
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java
|
|
index 89894bc6a55bc7e456a9d49ac48f6a8192b890ae..f0eb5e0c01923f884b1c7c48e8d67ed5cd429854 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Fox.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java
|
|
@@ -35,6 +35,7 @@ import net.minecraft.util.RandomSource;
|
|
import net.minecraft.util.StringRepresentable;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.AgeableMob;
|
|
import net.minecraft.world.entity.Entity;
|
|
@@ -88,6 +89,7 @@ import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.CaveVines;
|
|
import net.minecraft.world.level.block.SweetBerryBushBlock;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.pathfinder.BlockPathTypes;
|
|
import net.minecraft.world.phys.Vec3;
|
|
|
|
@@ -141,6 +143,64 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
this.setCanPickUpLoot(true);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.foxRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.foxRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.foxControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ return getRider() != null && this.isControllable() ? 0.5F : super.getJumpPower();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setCanPickUpLoot(false);
|
|
+ clearStates();
|
|
+ setIsPouncing(false);
|
|
+ spitOutItem(getItemBySlot(EquipmentSlot.MAINHAND));
|
|
+ setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onDismount(Player rider) {
|
|
+ super.onDismount(rider);
|
|
+ setCanPickUpLoot(true);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.foxMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.foxBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.foxTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.foxAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
@@ -160,6 +220,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
return entityliving instanceof AbstractSchoolingFish;
|
|
});
|
|
this.goalSelector.addGoal(0, new Fox.FoxFloatGoal());
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new ClimbOnTopOfPowderSnowGoal(this, this.level));
|
|
this.goalSelector.addGoal(1, new Fox.FaceplantGoal());
|
|
this.goalSelector.addGoal(2, new Fox.FoxPanicGoal(2.2D));
|
|
@@ -186,6 +247,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
this.goalSelector.addGoal(11, new Fox.FoxSearchForItemsGoal());
|
|
this.goalSelector.addGoal(12, new Fox.FoxLookAtPlayerGoal(this, Player.class, 24.0F));
|
|
this.goalSelector.addGoal(13, new Fox.PerchAndSearchGoal());
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(3, new Fox.DefendTrustedTargetGoal(LivingEntity.class, false, false, (entityliving) -> {
|
|
return Fox.TRUSTED_TARGET_SELECTOR.test(entityliving) && !this.trusts(entityliving.getUUID());
|
|
}));
|
|
@@ -342,6 +404,11 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
}
|
|
|
|
private void setTargetGoals() {
|
|
+ // Purpur start - do not add duplicate goals
|
|
+ this.targetSelector.removeGoal(this.landTargetGoal);
|
|
+ this.targetSelector.removeGoal(this.turtleEggTargetGoal);
|
|
+ this.targetSelector.removeGoal(this.fishTargetGoal);
|
|
+ // Purpur end
|
|
if (this.getVariant() == Fox.Type.RED) {
|
|
this.targetSelector.addGoal(4, this.landTargetGoal);
|
|
this.targetSelector.addGoal(4, this.turtleEggTargetGoal);
|
|
@@ -375,6 +442,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
|
|
public void setVariant(Fox.Type variant) {
|
|
this.entityData.set(Fox.DATA_TYPE_ID, variant.getId());
|
|
+ this.setTargetGoals(); // Purpur - fix API bug not updating pathfinders on type change
|
|
}
|
|
|
|
List<UUID> getTrustedUUIDs() {
|
|
@@ -711,6 +779,29 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
return this.getTrustedUUIDs().contains(uuid);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ if (level.purpurConfig.foxTypeChangesWithTulips) {
|
|
+ ItemStack itemstack = player.getItemInHand(hand);
|
|
+ if (getVariant() == Type.RED && itemstack.getItem() == Items.WHITE_TULIP) {
|
|
+ setVariant(Type.SNOW);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ } else if (getVariant() == Type.SNOW && itemstack.getItem() == Items.ORANGE_TULIP) {
|
|
+ setVariant(Type.RED);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ return super.mobInteract(player, hand);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
// Paper start - Cancellable death event
|
|
protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource source) {
|
|
@@ -758,16 +849,16 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
return new Vec3(0.0D, (double) (0.55F * this.getEyeHeight()), (double) (this.getBbWidth() * 0.4F));
|
|
}
|
|
|
|
- public class FoxLookControl extends LookControl {
|
|
+ public class FoxLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
public FoxLookControl() {
|
|
super(Fox.this);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (!Fox.this.isSleeping()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -778,16 +869,16 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
}
|
|
}
|
|
|
|
- private class FoxMoveControl extends MoveControl {
|
|
+ private class FoxMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
public FoxMoveControl() {
|
|
super(Fox.this);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (Fox.this.canMove()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -905,8 +996,10 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer2, this.animal, this.partner, entityfox);
|
|
}
|
|
|
|
- this.animal.setAge(6000);
|
|
- this.partner.setAge(6000);
|
|
+ // Purpur start
|
|
+ this.animal.setAge(this.animal.getPurpurBreedTime());
|
|
+ this.partner.setAge(this.partner.getPurpurBreedTime());
|
|
+ // Purpur end
|
|
this.animal.resetLove();
|
|
this.partner.resetLove();
|
|
worldserver.addFreshEntityWithPassengers(entityfox, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason
|
|
@@ -1294,7 +1387,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
|
|
}
|
|
|
|
protected void onReachedTarget() {
|
|
- if (Fox.this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (Fox.this.level.purpurConfig.foxBypassMobGriefing || Fox.this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
BlockState iblockdata = Fox.this.level.getBlockState(this.blockPos);
|
|
|
|
if (iblockdata.is(Blocks.SWEET_BERRY_BUSH)) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
|
|
index 4fbbd74cda7e4f2c623db46c2c94d9697ca5df05..b5f445750a5ccbe7658396bdcc9648dc41f39ced 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
|
|
@@ -63,14 +63,59 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
private int remainingPersistentAngerTime;
|
|
@Nullable
|
|
private UUID persistentAngerTarget;
|
|
+ @Nullable private UUID summoner; // Purpur
|
|
|
|
public IronGolem(EntityType<? extends IronGolem> type, Level world) {
|
|
super(type, world);
|
|
this.setMaxUpStep(1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.ironGolemRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.ironGolemRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.ironGolemControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ironGolemMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.ironGolemTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public UUID getSummoner() {
|
|
+ return summoner;
|
|
+ }
|
|
+
|
|
+ public void setSummoner(@Nullable UUID summoner) {
|
|
+ this.summoner = summoner;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.ironGolemAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ if (level.purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ if (this.level.purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
|
|
this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F));
|
|
this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6D, false));
|
|
@@ -78,6 +123,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
this.goalSelector.addGoal(5, new OfferFlowerGoal(this));
|
|
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this));
|
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
|
|
@@ -148,6 +194,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putBoolean("PlayerCreated", this.isPlayerCreated());
|
|
+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur
|
|
this.addPersistentAngerSaveData(nbt);
|
|
}
|
|
|
|
@@ -155,6 +202,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
public void readAdditionalSaveData(CompoundTag nbt) {
|
|
super.readAdditionalSaveData(nbt);
|
|
this.setPlayerCreated(nbt.getBoolean("PlayerCreated"));
|
|
+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur
|
|
this.readPersistentAngerSaveData(this.level, nbt);
|
|
}
|
|
|
|
@@ -279,13 +327,13 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (!itemstack.is(Items.IRON_INGOT)) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
} else {
|
|
float f = this.getHealth();
|
|
|
|
this.heal(25.0F);
|
|
if (this.getHealth() == f) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
} else {
|
|
float f1 = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
|
|
|
|
@@ -294,6 +342,8 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
|
|
itemstack.shrink(1);
|
|
}
|
|
|
|
+ if (this.level.purpurConfig.ironGolemHealCalm && isAngry() && getHealth() == getMaxHealth()) stopBeingAngry(); // Purpur
|
|
+
|
|
return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
|
|
index 68a5ee85e64802e4509ba0d184fc0ceb3cbe2d11..b5d0d3aaa0442b4753aef8fdf8f85f017e1dd811 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
|
|
@@ -63,6 +63,43 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.mooshroomRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.mooshroomRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.mooshroomControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.mooshroomMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.mooshroomBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.mooshroomTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.mooshroomAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public float getWalkTargetValue(BlockPos pos, LevelReader world) {
|
|
return world.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : world.getPathfindingCostFromLightLevels(pos);
|
|
@@ -124,10 +161,10 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
} else if (itemstack.is(Items.SHEARS) && this.readyForShearing()) {
|
|
// CraftBukkit start
|
|
if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
- this.shear(SoundSource.PLAYERS);
|
|
+ this.shear(SoundSource.PLAYERS, net.minecraft.world.item.enchantment.EnchantmentHelper.getMobLooting(player)); // Purpur
|
|
this.gameEvent(GameEvent.SHEAR, player);
|
|
if (!this.level.isClientSide) {
|
|
itemstack.hurtAndBreak(1, player, (entityhuman1) -> {
|
|
@@ -145,7 +182,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
Optional<Pair<MobEffect, Integer>> optional = this.getEffectFromItemStack(itemstack);
|
|
|
|
if (!optional.isPresent()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
|
|
Pair<MobEffect, Integer> pair = (Pair) optional.get();
|
|
@@ -170,7 +207,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
}
|
|
|
|
@Override
|
|
- public void shear(SoundSource shearedSoundCategory) {
|
|
+ public void shear(SoundSource shearedSoundCategory, int looting) { // Purpur
|
|
this.level.playSound((Player) null, (Entity) this, SoundEvents.MOOSHROOM_SHEAR, shearedSoundCategory, 1.0F, 1.0F);
|
|
if (!this.level.isClientSide()) {
|
|
Cow entitycow = (Cow) EntityType.COW.create(this.level);
|
|
@@ -180,7 +217,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
// this.discard(); // CraftBukkit - moved down
|
|
entitycow.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
|
entitycow.setHealth(this.getHealth());
|
|
+ // Purpur start
|
|
+ entitycow.copyPosition(this);
|
|
entitycow.yBodyRot = this.yBodyRot;
|
|
+ entitycow.setYHeadRot(this.getYHeadRot());
|
|
+ entitycow.yRotO = this.yRotO;
|
|
+ entitycow.xRotO = this.xRotO;
|
|
+ // Purpur end
|
|
if (this.hasCustomName()) {
|
|
entitycow.setCustomName(this.getCustomName());
|
|
entitycow.setCustomNameVisible(this.isCustomNameVisible());
|
|
@@ -200,7 +243,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
|
|
this.discard(); // CraftBukkit - from above
|
|
// CraftBukkit end
|
|
|
|
- for (int i = 0; i < 5; ++i) {
|
|
+ for (int i = 0; i < 5 + (org.purpurmc.purpur.PurpurConfig.allowShearsLooting ? looting : 0); ++i) {
|
|
// CraftBukkit start
|
|
ItemEntity entityitem = new ItemEntity(this.level, this.getX(), this.getY(1.0D), this.getZ(), new ItemStack(this.getVariant().blockState.getBlock()));
|
|
EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
|
|
index f889e86bd6355ed72b85bf322d09ac2be4fb4954..f8d1afcd14ecc10b48b2902eb906872a81cca609 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
|
|
@@ -68,6 +68,43 @@ public class Ocelot extends Animal {
|
|
this.reassessTrustingGoals();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.ocelotRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.ocelotRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.ocelotControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ocelotMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.ocelotBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.ocelotTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.ocelotAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public boolean isTrusting() {
|
|
return (Boolean) this.entityData.get(Ocelot.DATA_TRUSTING);
|
|
}
|
|
@@ -99,12 +136,14 @@ public class Ocelot extends Animal {
|
|
protected void registerGoals() {
|
|
this.temptGoal = new Ocelot.OcelotTemptGoal(this, 0.6D, Ocelot.TEMPT_INGREDIENT, true);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(3, this.temptGoal);
|
|
this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3F));
|
|
this.goalSelector.addGoal(8, new OcelotAttackGoal(this));
|
|
this.goalSelector.addGoal(9, new BreedGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F));
|
|
this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 10.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Chicken.class, false));
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java
|
|
index 9c1e02c3a990cd0f8bba1c84c170b438278c02a7..d1e45052fc96b6f81a331c6c73cb68ff96238359 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Panda.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java
|
|
@@ -108,6 +108,53 @@ public class Panda extends Animal {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.pandaRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.pandaRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.pandaControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setForwardMot(0.0F);
|
|
+ sit(false);
|
|
+ eat(false);
|
|
+ setOnBack(false);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pandaMaxHealth);
|
|
+ setAttributes();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.pandaBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.pandaTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.pandaAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean canTakeItem(ItemStack stack) {
|
|
EquipmentSlot enumitemslot = Mob.getEquipmentSlotForItem(stack);
|
|
@@ -269,6 +316,7 @@ public class Panda extends Animal {
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0D));
|
|
this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2000000476837158D, true));
|
|
@@ -284,6 +332,7 @@ public class Panda extends Animal {
|
|
this.goalSelector.addGoal(12, new Panda.PandaRollGoal(this));
|
|
this.goalSelector.addGoal(13, new FollowParentGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(14, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new Panda.PandaHurtByTargetGoal(this, new Class[0])).setAlertOthers(new Class[0]));
|
|
}
|
|
|
|
@@ -607,7 +656,10 @@ public class Panda extends Animal {
|
|
|
|
public void setAttributes() {
|
|
if (this.isWeak()) {
|
|
- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(10.0D);
|
|
+ // Purpur start
|
|
+ net.minecraft.world.entity.ai.attributes.AttributeInstance maxHealth = this.getAttribute(Attributes.MAX_HEALTH);
|
|
+ maxHealth.setBaseValue(maxHealth.getValue() / 2);
|
|
+ // Purpur end
|
|
}
|
|
|
|
if (this.isLazy()) {
|
|
@@ -630,7 +682,7 @@ public class Panda extends Animal {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (this.isScared()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
} else if (this.isOnBack()) {
|
|
this.setOnBack(false);
|
|
return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
@@ -647,7 +699,7 @@ public class Panda extends Animal {
|
|
this.setInLove(player);
|
|
} else {
|
|
if (this.level.isClientSide || this.isSitting() || this.isInWater()) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
|
|
this.tryToSit();
|
|
@@ -666,7 +718,7 @@ public class Panda extends Animal {
|
|
|
|
return InteractionResult.SUCCESS;
|
|
} else {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
}
|
|
|
|
@@ -706,7 +758,7 @@ public class Panda extends Animal {
|
|
return !this.isOnBack() && !this.isScared() && !this.isEating() && !this.isRolling() && !this.isSitting();
|
|
}
|
|
|
|
- private static class PandaMoveControl extends MoveControl {
|
|
+ private static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private final Panda panda;
|
|
|
|
@@ -716,9 +768,9 @@ public class Panda extends Animal {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.panda.canPerformAction()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
|
|
index e6e40770acf71b9079e8f6ac07025319dd8e2e4e..8ca75f748ac7dcf872b5677648ba384992242a07 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
|
|
@@ -129,12 +129,88 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
public Parrot(EntityType<? extends Parrot> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new FlyingMoveControl(this, 10, false);
|
|
+ // Purpur start
|
|
+ final org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F);
|
|
+ class ParrotMoveControl extends FlyingMoveControl {
|
|
+ public ParrotMoveControl(Mob entity, int maxPitchChange, boolean noGravity) {
|
|
+ super(entity, maxPitchChange, noGravity);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ flyingController.purpurTick(mob.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasWanted() {
|
|
+ return mob.getRider() != null && mob.isControllable() ? getForwardMot() != 0 || getStrafeMot() != 0 : super.hasWanted();
|
|
+ }
|
|
+ }
|
|
+ this.moveControl = new ParrotMoveControl(this, 10, false);
|
|
+ // Purpur end
|
|
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, -1.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.parrotRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.parrotRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.parrotControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.parrotMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.parrotMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return 6000;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.parrotTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.parrotAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) {
|
|
@@ -153,8 +229,11 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
- this.goalSelector.addGoal(0, new PanicGoal(this, 1.25D));
|
|
+ //this.goalSelector.addGoal(0, new PanicGoal(this, 1.25D)); // Purpur - move down
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D)); // Purpur
|
|
+ if (this.level.purpurConfig.parrotBreedable) this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D)); // Purpur
|
|
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
|
this.goalSelector.addGoal(2, new FollowOwnerGoal(this, 1.0D, 5.0F, 1.0F, true));
|
|
@@ -261,7 +340,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
}
|
|
|
|
if (!this.level.isClientSide) {
|
|
- if (this.random.nextInt(10) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit
|
|
+ if ((this.level.purpurConfig.alwaysTameInCreative && player.getAbilities().instabuild) || (this.random.nextInt(10) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled())) { // CraftBukkit // Purpur
|
|
this.tame(player);
|
|
this.level.broadcastEntityEvent(this, (byte) 7);
|
|
} else {
|
|
@@ -269,6 +348,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
}
|
|
}
|
|
|
|
+ if (this.level.purpurConfig.parrotBreedable) return super.mobInteract(player, hand); // Purpur
|
|
return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
} else if (itemstack.is(Parrot.POISONOUS_FOOD)) {
|
|
if (!player.getAbilities().instabuild) {
|
|
@@ -294,7 +374,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
@Override
|
|
public boolean isFood(ItemStack stack) {
|
|
- return false;
|
|
+ return this.level.purpurConfig.parrotBreedable && Parrot.TAME_FOOD.contains(stack.getItem()); // Purpur
|
|
}
|
|
|
|
public static boolean checkParrotSpawnRules(EntityType<Parrot> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
|
@@ -306,13 +386,13 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
|
|
|
|
@Override
|
|
public boolean canMate(Animal other) {
|
|
- return false;
|
|
+ return super.canMate(other); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) {
|
|
- return null;
|
|
+ return world.purpurConfig.parrotBreedable ? EntityType.PARROT.create(world) : null; // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pig.java b/src/main/java/net/minecraft/world/entity/animal/Pig.java
|
|
index ee2dc361ecae87c9d4e2d038068934717c5a8b16..c6fe90852c88e138492c28296869308de8b8428f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Pig.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Pig.java
|
|
@@ -64,9 +64,47 @@ public class Pig extends Animal implements ItemSteerable, Saddleable {
|
|
this.steering = new ItemBasedSteering(this.entityData, Pig.DATA_BOOST_TIME, Pig.DATA_SADDLE_ID);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.pigRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.pigRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.pigControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pigMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.pigBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.pigTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.pigAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(3, new BreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(4, new TemptGoal(this, 1.2D, Ingredient.of(Items.CARROT_ON_A_STICK), false));
|
|
@@ -151,6 +189,17 @@ public class Pig extends Animal implements ItemSteerable, Saddleable {
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
boolean flag = this.isFood(player.getItemInHand(hand));
|
|
|
|
+ if (level.purpurConfig.pigGiveSaddleBack && player.isSecondaryUseActive() && !flag && isSaddled() && !isVehicle()) {
|
|
+ this.steering.setSaddle(false);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ ItemStack saddle = new ItemStack(Items.SADDLE);
|
|
+ if (!player.getInventory().add(saddle)) {
|
|
+ player.drop(saddle, false);
|
|
+ }
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+
|
|
if (!flag && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) {
|
|
if (!this.level.isClientSide) {
|
|
player.startRiding(this);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
|
|
index 41123a1ec1b410428d804c6f80fa969c4946608f..92437cd638f01506c09448bcd0d1ec6395c4cb07 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
|
|
@@ -60,11 +60,81 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
private int remainingPersistentAngerTime;
|
|
@Nullable
|
|
private UUID persistentAngerTarget;
|
|
+ private int standTimer = 0; // Purpur
|
|
|
|
public PolarBear(EntityType<? extends PolarBear> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.polarBearRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.polarBearRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.polarBearControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (!isStanding()) {
|
|
+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0) {
|
|
+ setStanding(true);
|
|
+ playSound(SoundEvents.POLAR_BEAR_WARNING, 1.0F, 1.0F);
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.polarBearMaxHealth);
|
|
+ }
|
|
+
|
|
+ public boolean canMate(Animal other) {
|
|
+ if (other == this) {
|
|
+ return false;
|
|
+ } else if (this.isStanding()) {
|
|
+ return false;
|
|
+ } else if (this.getTarget() != null) {
|
|
+ return false;
|
|
+ } else if (!(other instanceof PolarBear)) {
|
|
+ return false;
|
|
+ } else {
|
|
+ PolarBear bear = (PolarBear) other;
|
|
+ if (bear.isStanding()) {
|
|
+ return false;
|
|
+ }
|
|
+ if (bear.getTarget() != null) {
|
|
+ return false;
|
|
+ }
|
|
+ return this.isInLove() && bear.isInLove();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.polarBearBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.polarBearTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.polarBearAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) {
|
|
@@ -73,19 +143,27 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean isFood(ItemStack stack) {
|
|
- return false;
|
|
+ return level.purpurConfig.polarBearBreedableItem != null && stack.getItem() == level.purpurConfig.polarBearBreedableItem; // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal());
|
|
this.goalSelector.addGoal(1, new PolarBear.PolarBearPanicGoal());
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.polarBearBreedableItem != null) {
|
|
+ this.goalSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D));
|
|
+ this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, net.minecraft.world.item.crafting.Ingredient.of(level.purpurConfig.polarBearBreedableItem), false));
|
|
+ }
|
|
+ // Purpur end
|
|
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new PolarBear.PolarBearHurtByTargetGoal());
|
|
this.targetSelector.addGoal(2, new PolarBear.PolarBearAttackPlayersGoal());
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
|
|
@@ -202,6 +280,11 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
this.updatePersistentAnger((ServerLevel)this.level, true);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (isStanding() && --standTimer <= 0) {
|
|
+ setStanding(false);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -231,6 +314,7 @@ public class PolarBear extends Animal implements NeutralMob {
|
|
|
|
public void setStanding(boolean warning) {
|
|
this.entityData.set(DATA_STANDING_ID, warning);
|
|
+ standTimer = warning ? 20 : -1; // Purpur
|
|
}
|
|
|
|
public float getStandingAnimationScale(float tickDelta) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
|
|
index 9aa5aa0d66257bf1413a904c516293aea30d2ca8..d152c50f17e2ab7a37b0c295c7f62e63889b8b76 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
|
|
@@ -45,6 +45,33 @@ public class Pufferfish extends AbstractFish {
|
|
this.refreshDimensions();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.pufferfishRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.pufferfishControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pufferfishMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.pufferfishTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.pufferfishAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
|
|
index 2e75066db6cf4903f04428b73c4e868988776920..3395bc1d9140ab5496ad998343a963ae12f630d6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
|
|
@@ -83,6 +83,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
private boolean wasOnGround;
|
|
private int jumpDelayTicks;
|
|
public int moreCarrotTicks;
|
|
+ private boolean actualJump; // Purpur
|
|
|
|
public Rabbit(EntityType<? extends Rabbit> type, Level world) {
|
|
super(type, world);
|
|
@@ -91,6 +92,71 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
this.initializePathFinderGoals(); // CraftBukkit - moved code
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.rabbitRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.rabbitRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.rabbitControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (onGround) {
|
|
+ actualJump = true;
|
|
+ jumpFromGround();
|
|
+ actualJump = false;
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ private void handleJumping() {
|
|
+ if (onGround) {
|
|
+ RabbitJumpControl jumpController = (RabbitJumpControl) jumpControl;
|
|
+ if (!wasOnGround) {
|
|
+ setJumping(false);
|
|
+ jumpController.setCanJump(false);
|
|
+ }
|
|
+ if (!jumpController.wantJump()) {
|
|
+ if (moveControl.hasWanted()) {
|
|
+ startJumping();
|
|
+ }
|
|
+ } else if (!jumpController.canJump()) {
|
|
+ jumpController.setCanJump(true);
|
|
+ }
|
|
+ }
|
|
+ wasOnGround = onGround;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.rabbitMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.rabbitBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.rabbitTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.rabbitAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// CraftBukkit start - code from constructor
|
|
public void initializePathFinderGoals(){
|
|
this.setSpeedModifier(0.0D);
|
|
@@ -100,6 +166,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
@Override
|
|
public void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level));
|
|
this.goalSelector.addGoal(1, new Rabbit.RabbitPanicGoal(this, 2.2D));
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 0.8D));
|
|
@@ -114,6 +181,13 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
|
|
@Override
|
|
protected float getJumpPower() {
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ if (getForwardMot() < 0) {
|
|
+ setSpeed(getForwardMot() * 2F);
|
|
+ }
|
|
+ return actualJump ? 0.5F : 0.3F;
|
|
+ }
|
|
+ // Purpur end
|
|
if (!this.horizontalCollision && (!this.moveControl.hasWanted() || this.moveControl.getWantedY() <= this.getY() + 0.5D)) {
|
|
Path pathentity = this.navigation.getPath();
|
|
|
|
@@ -132,7 +206,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
}
|
|
|
|
@Override
|
|
- protected void jumpFromGround() {
|
|
+ public void jumpFromGround() { // Purpur - protected -> public
|
|
super.jumpFromGround();
|
|
double d0 = this.moveControl.getSpeedModifier();
|
|
|
|
@@ -182,6 +256,13 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
|
|
@Override
|
|
public void customServerAiStep() {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ handleJumping();
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (this.jumpDelayTicks > 0) {
|
|
--this.jumpDelayTicks;
|
|
}
|
|
@@ -399,10 +480,23 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
}
|
|
|
|
this.setVariant(entityrabbit_variant);
|
|
+
|
|
+ // Purpur start
|
|
+ if (entityrabbit_variant != Variant.EVIL && world.getLevel().purpurConfig.rabbitNaturalToast > 0D && random.nextDouble() <= world.getLevel().purpurConfig.rabbitNaturalToast) {
|
|
+ setCustomName(Component.translatable("Toast"));
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData, entityNbt);
|
|
}
|
|
|
|
private static Rabbit.Variant getRandomRabbitVariant(LevelAccessor world, BlockPos pos) {
|
|
+ // Purpur start
|
|
+ Level level = world.getMinecraftWorld();
|
|
+ if (level.purpurConfig.rabbitNaturalKiller > 0D && world.getRandom().nextDouble() <= level.purpurConfig.rabbitNaturalKiller) {
|
|
+ return Rabbit.Variant.EVIL;
|
|
+ }
|
|
+ // Purpur end
|
|
Holder<Biome> holder = world.getBiome(pos);
|
|
int i = world.getRandom().nextInt(100);
|
|
|
|
@@ -466,7 +560,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
}
|
|
}
|
|
|
|
- private static class RabbitMoveControl extends MoveControl {
|
|
+ private static class RabbitMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private final Rabbit rabbit;
|
|
private double nextJumpSpeed;
|
|
@@ -477,14 +571,14 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.rabbit.onGround && !this.rabbit.jumping && !((Rabbit.RabbitJumpControl) this.rabbit.jumpControl).wantJump()) {
|
|
this.rabbit.setSpeedModifier(0.0D);
|
|
} else if (this.hasWanted()) {
|
|
this.rabbit.setSpeedModifier(this.nextJumpSpeed);
|
|
}
|
|
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -546,7 +640,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
|
|
@Override
|
|
public boolean canUse() {
|
|
if (this.nextStartTick <= 0) {
|
|
- if (!this.rabbit.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!this.rabbit.level.purpurConfig.rabbitBypassMobGriefing && !this.rabbit.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
|
|
index 0af79daa357f53a8871e293b57e16c099e5d3f64..e0da8d1974f88e1426034620f78a29f9bdb5adf4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
|
|
@@ -13,6 +13,33 @@ public class Salmon extends AbstractSchoolingFish {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.salmonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.salmonControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.salmonMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.salmonTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.salmonAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public int getMaxSchoolSize() {
|
|
return 5;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
|
|
index fb3777e158065a6ce306a2a6e66bec053da2aeb4..9399361c8d26a140fa6042988a30a1d3d552e418 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
|
|
@@ -116,10 +116,48 @@ public class Sheep extends Animal implements Shearable {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.sheepRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.sheepRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.sheepControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.sheepMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.sheepBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.sheepTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.sheepAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.eatBlockGoal = new EatBlockGoal(this);
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D));
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new TemptGoal(this, 1.1D, Ingredient.of(Items.WHEAT), false));
|
|
@@ -254,7 +292,7 @@ public class Sheep extends Animal implements Shearable {
|
|
return InteractionResult.PASS;
|
|
}
|
|
// CraftBukkit end
|
|
- this.shear(SoundSource.PLAYERS);
|
|
+ this.shear(SoundSource.PLAYERS, net.minecraft.world.item.enchantment.EnchantmentHelper.getMobLooting(player)); // Purpur
|
|
this.gameEvent(GameEvent.SHEAR, player);
|
|
itemstack.hurtAndBreak(1, player, (entityhuman1) -> {
|
|
entityhuman1.broadcastBreakEvent(hand);
|
|
@@ -269,14 +307,15 @@ public class Sheep extends Animal implements Shearable {
|
|
}
|
|
|
|
@Override
|
|
- public void shear(SoundSource shearedSoundCategory) {
|
|
+ public void shear(SoundSource shearedSoundCategory, int looting) { // Purpur
|
|
this.level.playSound((Player) null, (Entity) this, SoundEvents.SHEEP_SHEAR, shearedSoundCategory, 1.0F, 1.0F);
|
|
this.setSheared(true);
|
|
int i = 1 + this.random.nextInt(3);
|
|
+ if (org.purpurmc.purpur.PurpurConfig.allowShearsLooting) i += looting; // Purpur
|
|
|
|
for (int j = 0; j < i; ++j) {
|
|
this.forceDrops = true; // CraftBukkit
|
|
- ItemEntity entityitem = this.spawnAtLocation((ItemLike) Sheep.ITEM_BY_DYE.get(this.getColor()), 1);
|
|
+ ItemEntity entityitem = this.spawnAtLocation((ItemLike) Sheep.ITEM_BY_DYE.get(this.level.purpurConfig.sheepShearJebRandomColor && hasCustomName() && getCustomName().getString().equals("jeb_") ? DyeColor.random(this.level.random) : this.getColor()), 1); // Purpur
|
|
this.forceDrops = false; // CraftBukkit
|
|
|
|
if (entityitem != null) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
|
|
index 5437571ce76c62e9cae841e99127867fffb39f43..34fa428268a863e8e36b6340a482ec67f1199efb 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
|
|
@@ -49,17 +49,56 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
private static final EntityDataAccessor<Byte> DATA_PUMPKIN_ID = SynchedEntityData.defineId(SnowGolem.class, EntityDataSerializers.BYTE);
|
|
private static final byte PUMPKIN_FLAG = 16;
|
|
private static final float EYE_HEIGHT = 1.7F;
|
|
+ @Nullable private java.util.UUID summoner; // Purpur
|
|
|
|
public SnowGolem(EntityType<? extends SnowGolem> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.snowGolemRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.snowGolemRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.snowGolemControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.snowGolemMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return summoner;
|
|
+ }
|
|
+
|
|
+ public void setSummoner(@Nullable java.util.UUID summoner) {
|
|
+ this.summoner = summoner;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.snowGolemAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
- this.goalSelector.addGoal(1, new RangedAttackGoal(this, 1.25D, 20, 10.0F));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.goalSelector.addGoal(1, new RangedAttackGoal(this, level.purpurConfig.snowGolemAttackDistance, level.purpurConfig.snowGolemSnowBallMin, level.purpurConfig.snowGolemSnowBallMax, level.purpurConfig.snowGolemSnowBallModifier)); // Purpur
|
|
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F));
|
|
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (entityliving) -> {
|
|
return entityliving instanceof Enemy;
|
|
}));
|
|
@@ -79,6 +118,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putBoolean("Pumpkin", this.hasPumpkin());
|
|
+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -87,12 +127,13 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
if (nbt.contains("Pumpkin")) {
|
|
this.setPumpkin(nbt.getBoolean("Pumpkin"));
|
|
}
|
|
+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur
|
|
|
|
}
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level.purpurConfig.snowGolemTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -103,10 +144,11 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
this.hurt(this.damageSources().melting, 1.0F); // CraftBukkit - DamageSource.BURN -> CraftEventFactory.MELTING
|
|
}
|
|
|
|
- if (!this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (!this.level.purpurConfig.snowGolemBypassMobGriefing && !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return;
|
|
}
|
|
|
|
+ if (getRider() != null && this.isControllable() && !level.purpurConfig.snowGolemLeaveTrailWhenRidden) return; // Purpur - don't leave snow trail when being ridden
|
|
BlockState iblockdata = Blocks.SNOW.defaultBlockState();
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
@@ -154,10 +196,10 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
if (itemstack.is(Items.SHEARS) && this.readyForShearing()) {
|
|
// CraftBukkit start
|
|
if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
- this.shear(SoundSource.PLAYERS);
|
|
+ this.shear(SoundSource.PLAYERS, net.minecraft.world.item.enchantment.EnchantmentHelper.getMobLooting(player)); // Purpur
|
|
this.gameEvent(GameEvent.SHEAR, player);
|
|
if (!this.level.isClientSide) {
|
|
itemstack.hurtAndBreak(1, player, (entityhuman1) -> {
|
|
@@ -166,17 +208,27 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
|
|
}
|
|
|
|
return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
+ // Purpur start
|
|
+ } else if (level.purpurConfig.snowGolemPutPumpkinBack && !hasPumpkin() && itemstack.getItem() == Blocks.CARVED_PUMPKIN.asItem()) {
|
|
+ setPumpkin(true);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ // Purpur end
|
|
} else {
|
|
- return InteractionResult.PASS;
|
|
+ return tryRide(player, hand); // Purpur
|
|
}
|
|
}
|
|
|
|
@Override
|
|
- public void shear(SoundSource shearedSoundCategory) {
|
|
+ public void shear(SoundSource shearedSoundCategory, int looting) { // Purpur
|
|
this.level.playSound((Player) null, (Entity) this, SoundEvents.SNOW_GOLEM_SHEAR, shearedSoundCategory, 1.0F, 1.0F);
|
|
if (!this.level.isClientSide()) {
|
|
this.setPumpkin(false);
|
|
this.forceDrops = true; // CraftBukkit
|
|
+ if (level.purpurConfig.snowGolemDropsPumpkin) // Purpur
|
|
+ for (int i = 0; i < 1 + (org.purpurmc.purpur.PurpurConfig.allowShearsLooting ? looting : 0); i++) // Purpur
|
|
this.spawnAtLocation(new ItemStack(Items.CARVED_PUMPKIN), 1.7F);
|
|
this.forceDrops = false; // CraftBukkit
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java
|
|
index 72eea6e512060fc622ca79ca87437f19a64604cc..31c89a6b8f766e1fd03608723c2d03f7f64e2e9b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Squid.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java
|
|
@@ -46,13 +46,66 @@ public class Squid extends WaterAnimal {
|
|
|
|
public Squid(EntityType<? extends Squid> type, Level world) {
|
|
super(type, world);
|
|
- //this.random.setSeed((long) this.getId()); // Paper - we set the random to shared, do not clobber the seed
|
|
+ if (!world.purpurConfig.entitySharedRandom) this.random.setSeed((long) this.getId()); // Paper - we set the random to shared, do not clobber the seed // Purpur
|
|
this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.squidRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.squidControllable;
|
|
+ }
|
|
+
|
|
+ protected void rotateVectorAroundY(org.bukkit.util.Vector vector, double degrees) {
|
|
+ double rad = Math.toRadians(degrees);
|
|
+ double cos = Math.cos(rad);
|
|
+ double sine = Math.sin(rad);
|
|
+ double x = vector.getX();
|
|
+ double z = vector.getZ();
|
|
+ vector.setX(cos * x - sine * z);
|
|
+ vector.setZ(sine * x + cos * z);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.squidMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.phys.AABB getAxisForFluidCheck() {
|
|
+ // Stops squids from floating just over the water
|
|
+ return super.getAxisForFluidCheck().offsetY(level.purpurConfig.squidOffsetWaterCheck);
|
|
+ }
|
|
+
|
|
+ public boolean canFly() {
|
|
+ return this.level.purpurConfig.squidsCanFly;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isInWater() {
|
|
+ return this.wasTouchingWater || canFly();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.squidTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.squidAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Squid.SquidFleeGoal());
|
|
}
|
|
|
|
@@ -121,6 +174,7 @@ public class Squid extends WaterAnimal {
|
|
}
|
|
|
|
if (this.isInWaterOrBubble()) {
|
|
+ if (canFly()) setNoGravity(!wasTouchingWater); // Purpur
|
|
if (this.tentacleMovement < 3.1415927F) {
|
|
float f = this.tentacleMovement / 3.1415927F;
|
|
|
|
@@ -244,11 +298,43 @@ public class Squid extends WaterAnimal {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ Player rider = squid.getRider();
|
|
+ if (rider != null && squid.isControllable()) {
|
|
+ if (rider.jumping) {
|
|
+ squid.onSpacebar();
|
|
+ }
|
|
+ float forward = rider.getForwardMot();
|
|
+ float strafe = rider.getStrafeMot();
|
|
+ float speed = (float) squid.getAttributeValue(Attributes.MOVEMENT_SPEED) * 10F;
|
|
+ if (forward < 0.0F) {
|
|
+ speed *= -0.5;
|
|
+ }
|
|
+ org.bukkit.util.Vector dir = rider.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(speed / 20.0F);
|
|
+ if (strafe != 0.0F) {
|
|
+ if (forward == 0.0F) {
|
|
+ dir.setY(0);
|
|
+ rotateVectorAroundY(dir, strafe > 0.0F ? -90 : 90);
|
|
+ } else if (forward < 0.0F) {
|
|
+ rotateVectorAroundY(dir, strafe > 0.0F ? 45 : -45);
|
|
+ } else {
|
|
+ rotateVectorAroundY(dir, strafe > 0.0F ? -45 : 45);
|
|
+ }
|
|
+ }
|
|
+ if (forward != 0.0F || strafe != 0.0F) {
|
|
+ squid.setMovementVector((float) dir.getX(), (float) dir.getY(), (float) dir.getZ());
|
|
+ } else {
|
|
+ squid.setMovementVector(0.0F, 0.0F, 0.0F);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
int i = this.squid.getNoActionTime();
|
|
|
|
if (i > 100) {
|
|
this.squid.setMovementVector(0.0F, 0.0F, 0.0F);
|
|
- } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.wasTouchingWater || !this.squid.hasMovementVector()) {
|
|
+ } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.isInWater() || !this.squid.hasMovementVector()) { // Purpur
|
|
float f = this.squid.getRandom().nextFloat() * 6.2831855F;
|
|
float f1 = Mth.cos(f) * 0.2F;
|
|
float f2 = -0.1F + this.squid.getRandom().nextFloat() * 0.2F;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
|
|
index b05b560b7570e97bc234b75f26233909fcf575b3..e4b4bf5ef228c0460fdab966d4c9b5c428f78b9a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
|
|
@@ -42,6 +42,33 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.tropicalFishRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.tropicalFishControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.tropicalFishMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.tropicalFishTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.tropicalFishAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static String getPredefinedName(int variant) {
|
|
return "entity.minecraft.tropical_fish.predefined." + variant;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
index 81dab77f525ae667614f940c4ff5ec308a9579a2..52eff7a4d3a34a566bc3bc03e6643c494c757156 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
|
|
@@ -83,6 +83,43 @@ public class Turtle extends Animal {
|
|
this.setMaxUpStep(1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.turtleRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.turtleRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.turtleControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.turtleMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.turtleBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.turtleTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.turtleAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void setHomePos(BlockPos pos) {
|
|
this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos...
|
|
}
|
|
@@ -185,6 +222,7 @@ public class Turtle extends Animal {
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new Turtle.TurtlePanicGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0D));
|
|
@@ -342,13 +380,15 @@ public class Turtle extends Animal {
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = null; // CraftBukkit
|
|
}
|
|
|
|
- private static class TurtleMoveControl extends MoveControl {
|
|
+ private static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private final Turtle turtle;
|
|
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur
|
|
|
|
TurtleMoveControl(Turtle turtle) {
|
|
super(turtle);
|
|
this.turtle = turtle;
|
|
+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(turtle, 0.25D); // Purpur
|
|
}
|
|
|
|
private void updateSpeed() {
|
|
@@ -367,8 +407,18 @@ public class Turtle extends Animal {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void purpurTick(Player rider) {
|
|
+ if (turtle.isInWater()) {
|
|
+ waterController.purpurTick(rider);
|
|
+ } else {
|
|
+ super.purpurTick(rider);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
this.updateSpeed();
|
|
if (this.operation == MoveControl.Operation.MOVE_TO && !this.turtle.getNavigation().isDone()) {
|
|
double d0 = this.wantedX - this.turtle.getX();
|
|
@@ -384,7 +434,7 @@ public class Turtle extends Animal {
|
|
|
|
this.turtle.setYRot(this.rotlerp(this.turtle.getYRot(), f, 90.0F));
|
|
this.turtle.yBodyRot = this.turtle.getYRot();
|
|
- float f1 = (float) (this.speedModifier * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float f1 = (float) (this.getSpeedModifier() * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
|
|
this.turtle.setSpeed(Mth.lerp(0.125F, this.turtle.getSpeed(), f1));
|
|
this.turtle.setDeltaMovement(this.turtle.getDeltaMovement().add(0.0D, (double) this.turtle.getSpeed() * d1 * 0.1D, 0.0D));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java
|
|
index 35cfa366baf6747105faa93f1220bb9cc31a5bd5..ff3a6755d04f2280a36bd363ab1722e074e37194 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java
|
|
@@ -82,6 +82,6 @@ public abstract class WaterAnimal extends PathfinderMob {
|
|
i = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(i);
|
|
j = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(j);
|
|
// Paper end
|
|
- return pos.getY() >= j && pos.getY() <= i && world.getFluidState(pos.below()).is(FluidTags.WATER) && world.getBlockState(pos.above()).is(Blocks.WATER);
|
|
+ return ((reason == MobSpawnType.SPAWNER && world.getMinecraftWorld().purpurConfig.spawnerFixMC238526) || (pos.getY() >= j && pos.getY() <= i)) && world.getFluidState(pos.below()).is(FluidTags.WATER) && world.getBlockState(pos.above()).is(Blocks.WATER); // Purpur
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
|
|
index 612601b2536dc522356d4dd2c2ea1192f6435f72..e0ca657b0aea52ab6a91c256c1bfad1e5028f6e0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
|
|
@@ -10,6 +10,7 @@ import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.tags.BlockTags;
|
|
@@ -17,9 +18,12 @@ import net.minecraft.util.Mth;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.util.TimeUtil;
|
|
import net.minecraft.util.valueproviders.UniformInt;
|
|
+import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import net.minecraft.world.effect.MobEffects;
|
|
import net.minecraft.world.entity.AgeableMob;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntityDimensions;
|
|
@@ -29,6 +33,7 @@ import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.MobSpawnType;
|
|
import net.minecraft.world.entity.NeutralMob;
|
|
import net.minecraft.world.entity.Pose;
|
|
+import net.minecraft.world.entity.SpawnGroupData;
|
|
import net.minecraft.world.entity.TamableAnimal;
|
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
@@ -37,6 +42,7 @@ import net.minecraft.world.entity.ai.goal.BegGoal;
|
|
import net.minecraft.world.entity.ai.goal.BreedGoal;
|
|
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
|
import net.minecraft.world.entity.ai.goal.FollowOwnerGoal;
|
|
+import net.minecraft.world.entity.ai.goal.Goal;
|
|
import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal;
|
|
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
|
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
|
@@ -64,6 +70,7 @@ import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
+import net.minecraft.world.level.ServerLevelAccessor;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.pathfinder.BlockPathTypes;
|
|
@@ -83,6 +90,37 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
|
|
return entitytypes == EntityType.SHEEP || entitytypes == EntityType.RABBIT || entitytypes == EntityType.FOX;
|
|
};
|
|
+ // Purpur start - rabid wolf spawn chance
|
|
+ private boolean isRabid = false;
|
|
+ private static final Predicate<LivingEntity> RABID_PREDICATE = entity -> entity instanceof ServerPlayer || entity instanceof Mob;
|
|
+ private final Goal PATHFINDER_VANILLA = new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR);
|
|
+ private final Goal PATHFINDER_RABID = new NonTameRandomTargetGoal<>(this, LivingEntity.class, false, RABID_PREDICATE);
|
|
+ private static final class AvoidRabidWolfGoal extends AvoidEntityGoal<Wolf> {
|
|
+ private final Wolf wolf;
|
|
+
|
|
+ public AvoidRabidWolfGoal(Wolf wolf, float distance, double minSpeed, double maxSpeed) {
|
|
+ super(wolf, Wolf.class, distance, minSpeed, maxSpeed);
|
|
+ this.wolf = wolf;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return super.canUse() && !this.wolf.isRabid() && this.toAvoid != null && this.toAvoid.isRabid(); // wolves which are not rabid run away from rabid wolves
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ this.wolf.setTarget(null);
|
|
+ super.start();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ this.wolf.setTarget(null);
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
private static final float START_HEALTH = 8.0F;
|
|
private static final float TAME_HEALTH = 20.0F;
|
|
private float interestedAngle;
|
|
@@ -102,12 +140,93 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
this.setPathfindingMalus(BlockPathTypes.DANGER_POWDER_SNOW, -1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.wolfRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.wolfRidableInWater;
|
|
+ }
|
|
+
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setInSittingPose(false);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.wolfControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.wolfMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.wolfBreedingTicks;
|
|
+ }
|
|
+
|
|
+ public boolean isRabid() {
|
|
+ return this.isRabid;
|
|
+ }
|
|
+
|
|
+ public void setRabid(boolean isRabid) {
|
|
+ this.isRabid = isRabid;
|
|
+ updatePathfinders(true);
|
|
+ }
|
|
+
|
|
+ public void updatePathfinders(boolean modifyEffects) {
|
|
+ this.targetSelector.removeGoal(PATHFINDER_VANILLA);
|
|
+ this.targetSelector.removeGoal(PATHFINDER_RABID);
|
|
+ if (this.isRabid) {
|
|
+ setTame(false);
|
|
+ setOwnerUUID(null);
|
|
+ this.targetSelector.addGoal(5, PATHFINDER_RABID);
|
|
+ if (modifyEffects) this.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 1200));
|
|
+ } else {
|
|
+ this.targetSelector.addGoal(5, PATHFINDER_VANILLA);
|
|
+ this.stopBeingAngry();
|
|
+ if (modifyEffects) this.removeEffect(MobEffects.CONFUSION);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType type, @Nullable SpawnGroupData data, @Nullable CompoundTag nbt) {
|
|
+ this.isRabid = world.getLevel().purpurConfig.wolfNaturalRabid > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.wolfNaturalRabid;
|
|
+ this.updatePathfinders(false);
|
|
+ return super.finalizeSpawn(world, difficulty, type, data, nbt);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tame(Player player) {
|
|
+ setCollarColor(level.purpurConfig.wolfDefaultCollarColor);
|
|
+ super.tame(player);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.wolfTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.wolfAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Wolf.WolfPanicGoal(1.5D));
|
|
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
|
this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5D, 1.5D));
|
|
+ this.goalSelector.addGoal(3, new AvoidRabidWolfGoal(this, 24.0F, 1.5D, 1.5D)); // Purpur
|
|
this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4F));
|
|
this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0D, true));
|
|
this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0D, 10.0F, 2.0F, false));
|
|
@@ -116,11 +235,12 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
this.goalSelector.addGoal(9, new BegGoal(this, 8.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(10, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this));
|
|
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
|
|
this.targetSelector.addGoal(3, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
|
|
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
|
|
- this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, Wolf.PREY_SELECTOR));
|
|
+ // this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, Wolf.PREY_SELECTOR)); // Purpur - moved to updatePathfinders()
|
|
this.targetSelector.addGoal(6, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
this.targetSelector.addGoal(7, new NearestAttackableTargetGoal<>(this, AbstractSkeleton.class, false));
|
|
this.targetSelector.addGoal(8, new ResetUniversalAngerTargetGoal<>(this, true));
|
|
@@ -165,6 +285,7 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putByte("CollarColor", (byte) this.getCollarColor().getId());
|
|
+ nbt.putBoolean("Purpur.IsRabid", this.isRabid); // Purpur
|
|
this.addPersistentAngerSaveData(nbt);
|
|
}
|
|
|
|
@@ -174,6 +295,10 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
if (nbt.contains("CollarColor", 99)) {
|
|
this.setCollarColor(DyeColor.byId(nbt.getInt("CollarColor")));
|
|
}
|
|
+ // Purpur start
|
|
+ this.isRabid = nbt.getBoolean("Purpur.IsRabid");
|
|
+ this.updatePathfinders(false);
|
|
+ // Purpur end
|
|
|
|
this.readPersistentAngerSaveData(this.level, nbt);
|
|
}
|
|
@@ -218,6 +343,11 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
public void tick() {
|
|
super.tick();
|
|
if (this.isAlive()) {
|
|
+ // Purpur start
|
|
+ if (this.age % 300 == 0 && this.isRabid()) {
|
|
+ this.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 400));
|
|
+ }
|
|
+ // Purpur end
|
|
this.interestedAngleO = this.interestedAngle;
|
|
if (this.isInterested()) {
|
|
this.interestedAngle += (1.0F - this.interestedAngle) * 0.4F;
|
|
@@ -412,7 +542,7 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
}
|
|
|
|
// CraftBukkit - added event call and isCancelled check.
|
|
- if (this.random.nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) {
|
|
+ if ((this.level.purpurConfig.alwaysTameInCreative && player.getAbilities().instabuild) || (this.random.nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, player).isCancelled())) { // Purpur
|
|
this.tame(player);
|
|
this.navigation.stop();
|
|
this.setTarget((LivingEntity) null);
|
|
@@ -424,6 +554,20 @@ public class Wolf extends TamableAnimal implements NeutralMob {
|
|
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
+ // Purpur start
|
|
+ else if (this.level.purpurConfig.wolfMilkCuresRabies && itemstack.getItem() == Items.MILK_BUCKET && this.isRabid()) {
|
|
+ if (!player.isCreative()) {
|
|
+ player.setItemInHand(hand, new ItemStack(Items.BUCKET));
|
|
+ }
|
|
+ this.setRabid(false);
|
|
+ for (int i = 0; i < 10; ++i) {
|
|
+ ((ServerLevel) level).sendParticles(((ServerLevel) level).players(), null, ParticleTypes.HAPPY_VILLAGER,
|
|
+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 1.5), getZ() + random.nextFloat(), 1,
|
|
+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
return super.mobInteract(player, hand);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
|
|
index a41d8101c960163803179d3717889aee6182d0bb..e95540122ae6a486ce12a5f50fb4d2d073239554 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
|
|
@@ -101,10 +101,23 @@ public class Allay extends PathfinderMob implements InventoryCarrier {
|
|
private float spinningAnimationTicks;
|
|
private float spinningAnimationTicks0;
|
|
public boolean forceDancing = false; // CraftBukkit
|
|
+ private org.purpurmc.purpur.controller.FlyingMoveControllerWASD purpurController; // Purpur
|
|
|
|
public Allay(EntityType<? extends Allay> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new FlyingMoveControl(this, 20, true);
|
|
+ // Purpur start
|
|
+ this.purpurController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.1F, 0.5F);
|
|
+ this.moveControl = new FlyingMoveControl(this, 20, true) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ purpurController.purpurTick(mob.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.setCanPickUpLoot(this.canPickUpLoot());
|
|
EntityPositionSource entitypositionsource = new EntityPositionSource(this, this.getEyeHeight());
|
|
|
|
@@ -119,6 +132,28 @@ public class Allay extends PathfinderMob implements InventoryCarrier {
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.allayRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.allayRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.allayControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected Brain.Provider<Allay> brainProvider() {
|
|
return Brain.provider(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES);
|
|
@@ -226,13 +261,13 @@ public class Allay extends PathfinderMob implements InventoryCarrier {
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("allayBrain");
|
|
+ //this.level.getProfiler().push("allayBrain"); // Purpur
|
|
if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
this.getBrain().tick((ServerLevel) this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("allayActivityUpdate");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("allayActivityUpdate"); // Purpur
|
|
AllayAi.updateActivity(this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
super.customServerAiStep();
|
|
}
|
|
|
|
@@ -374,9 +409,31 @@ public class Allay extends PathfinderMob implements InventoryCarrier {
|
|
|
|
@Override
|
|
public boolean wantsToPickUp(ItemStack stack) {
|
|
- ItemStack itemstack1 = this.getItemInHand(InteractionHand.MAIN_HAND);
|
|
-
|
|
- return !itemstack1.isEmpty() && this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.inventory.canAddItem(stack) && this.allayConsidersItemEqual(itemstack1, stack);
|
|
+ // Purpur start
|
|
+ if (!this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ return false;
|
|
+ }
|
|
+ ItemStack itemStack = this.getItemInHand(InteractionHand.MAIN_HAND);
|
|
+ if (itemStack.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!allayConsidersItemEqual(itemStack, stack)) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!this.inventory.canAddItem(stack)) {
|
|
+ return false;
|
|
+ }
|
|
+ for (String tag : this.level.purpurConfig.allayRespectNBT) {
|
|
+ if (stack.hasTag() && itemStack.hasTag()) {
|
|
+ Tag tag1 = stack.getTag().get(tag);
|
|
+ Tag tag2 = itemStack.getTag().get(tag);
|
|
+ if (!Objects.equals(tag1, tag2)) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ // Purpur end
|
|
}
|
|
|
|
private boolean allayConsidersItemEqual(ItemStack stack, ItemStack stack2) {
|
|
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 38d21943fb2940f53c2d0ac2c3b94a6f0e46e700..18ce5389040b516a7061d2d8e104f400183fdeec 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
|
|
@@ -98,6 +98,43 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
|
|
this.setMaxUpStep(1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.axolotlRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.axolotlControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.axolotlMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.axolotlBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.axolotlTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.axolotlAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public Map<String, Vector3f> getModelRotationValues() {
|
|
return this.modelRotationValues;
|
|
@@ -288,13 +325,13 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("axolotlBrain");
|
|
+ //this.level.getProfiler().push("axolotlBrain"); // Purpur
|
|
if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
this.getBrain().tick((ServerLevel) this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("axolotlActivityUpdate");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("axolotlActivityUpdate"); // Purpur
|
|
AxolotlAi.updateActivity(this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
if (!this.isNoAi()) {
|
|
Optional<Integer> optional = this.getBrain().getMemory(MemoryModuleType.PLAY_DEAD_TICKS);
|
|
|
|
@@ -521,14 +558,22 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
|
|
private static class AxolotlMoveControl extends SmoothSwimmingMoveControl {
|
|
|
|
private final Axolotl axolotl;
|
|
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur
|
|
|
|
public AxolotlMoveControl(Axolotl axolotl) {
|
|
super(axolotl, 85, 10, 0.1F, 0.5F, false);
|
|
this.axolotl = axolotl;
|
|
+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(axolotl, 0.5D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (axolotl.getRider() != null && axolotl.isControllable()) {
|
|
+ waterController.purpurTick(axolotl.getRider());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
if (!this.axolotl.isPlayingDead()) {
|
|
super.tick();
|
|
}
|
|
@@ -543,9 +588,9 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (!Axolotl.this.isPlayingDead()) {
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
|
|
index 05c7680569346bb863b896bcc9515f3e7cfb8114..31922ac1139f34e0da61a719e3645c1aaa188890 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
|
|
@@ -83,6 +83,13 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider
|
|
groundPathNavigation.setCanWalkOverFences(true);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.camelBreedingTicks;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
@@ -149,13 +156,13 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("camelBrain");
|
|
+ //this.level.getProfiler().push("camelBrain"); // Purpur
|
|
Brain<Camel> brain = (Brain<Camel>) this.getBrain(); // Paper - decompile fix
|
|
brain.tick((ServerLevel)this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("camelActivityUpdate");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("camelActivityUpdate"); // Purpur
|
|
CamelAi.updateActivity(this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
super.customServerAiStep();
|
|
}
|
|
|
|
@@ -314,6 +321,23 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider
|
|
return this.dashCooldown;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.camelMaxHealthMin, this.level.purpurConfig.camelMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.camelJumpStrengthMin, this.level.purpurConfig.camelJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.camelMovementSpeedMin, this.level.purpurConfig.camelMovementSpeedMax);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.CAMEL_AMBIENT;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
|
|
index be44667b8205cd3bb1cefddf8e0e743535414e14..c355aaed76663d37a5da8b2f49f9808828b4ef9b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
|
|
@@ -82,16 +82,69 @@ public class Frog extends Animal implements VariantHolder<FrogVariant> {
|
|
public final AnimationState croakAnimationState = new AnimationState();
|
|
public final AnimationState tongueAnimationState = new AnimationState();
|
|
public final AnimationState swimIdleAnimationState = new AnimationState();
|
|
+ private org.purpurmc.purpur.controller.MoveControllerWASD purpurLandController; // Purpur
|
|
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur
|
|
|
|
public Frog(EntityType<? extends Animal> type, Level world) {
|
|
super(type, world);
|
|
this.lookControl = new Frog.FrogLookControl(this);
|
|
this.setPathfindingMalus(BlockPathTypes.WATER, 4.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.TRAPDOOR, -1.0F);
|
|
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur start
|
|
+ this.purpurLandController = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.2F);
|
|
+ this.purpurWaterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F);
|
|
+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ net.minecraft.world.entity.player.Player rider = mob.getRider();
|
|
+ if (rider != null && mob.isControllable()) {
|
|
+ if (mob.isInWater()) {
|
|
+ purpurWaterController.purpurTick(rider);
|
|
+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, -0.005D, 0.0D));
|
|
+ } else {
|
|
+ purpurLandController.purpurTick(rider);
|
|
+ }
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.setMaxUpStep(1.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.frogRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.frogRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.frogControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ return (getRider() != null && isControllable()) ? level.purpurConfig.frogRidableJumpHeight * this.getBlockJumpFactor() : super.getJumpPower();
|
|
+ }
|
|
+
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.frogBreedingTicks;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected Brain.Provider<Frog> brainProvider() {
|
|
return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
|
|
@@ -170,13 +223,13 @@ public class Frog extends Animal implements VariantHolder<FrogVariant> {
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("frogBrain");
|
|
+ //this.level.getProfiler().push("frogBrain"); // Purpur
|
|
if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
this.getBrain().tick((ServerLevel)this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("frogActivityUpdate");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("frogActivityUpdate"); // Purpur
|
|
FrogAi.updateActivity(this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
super.customServerAiStep();
|
|
}
|
|
|
|
@@ -376,7 +429,7 @@ public class Frog extends Animal implements VariantHolder<FrogVariant> {
|
|
return world.getBlockState(pos.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(world, pos);
|
|
}
|
|
|
|
- class FrogLookControl extends LookControl {
|
|
+ class FrogLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
FrogLookControl(Mob entity) {
|
|
super(entity);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
index e591b0a09f5a8475b3ec9cd28bd5d5b69809ed73..aadc6743deb195ac3368548a75be641ffd3da404 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
@@ -45,13 +45,50 @@ public class Tadpole extends AbstractFish {
|
|
protected static final ImmutableList<SensorType<? extends Sensor<? super Tadpole>>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS);
|
|
protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.PATH, MemoryModuleType.NEAREST_VISIBLE_ADULT, MemoryModuleType.TEMPTATION_COOLDOWN_TICKS, MemoryModuleType.IS_TEMPTED, MemoryModuleType.TEMPTING_PLAYER, MemoryModuleType.BREED_TARGET, MemoryModuleType.IS_PANICKING);
|
|
public boolean ageLocked; // Paper
|
|
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurController; // Purpur
|
|
|
|
public Tadpole(EntityType<? extends AbstractFish> type, Level world) {
|
|
super(type, world);
|
|
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
|
|
+ // Purpur start
|
|
+ this.purpurController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F);
|
|
+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ Player rider = mob.getRider();
|
|
+ if (rider != null && mob.isControllable()) {
|
|
+ purpurController.purpurTick(rider);
|
|
+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, 0.002D, 0.0D));
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.lookControl = new SmoothSwimmingLookControl(this, 10);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.tadpoleRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.tadpoleRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.tadpoleControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected PathNavigation createNavigation(Level world) {
|
|
return new WaterBoundPathNavigation(this, world);
|
|
@@ -80,13 +117,13 @@ public class Tadpole extends AbstractFish {
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("tadpoleBrain");
|
|
+ //this.level.getProfiler().push("tadpoleBrain"); // Purpur
|
|
if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
this.getBrain().tick((ServerLevel) this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("tadpoleActivityUpdate");
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
+ //this.level.getProfiler().push("tadpoleActivityUpdate"); // Purpur
|
|
TadpoleAi.updateActivity(this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
super.customServerAiStep();
|
|
}
|
|
|
|
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 4fdc3bd591b6df4c28901e4571aa23baf034d885..f5c0fc9f30bdf7935200b875ada0ff1011fdb034 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
|
|
@@ -89,6 +89,38 @@ public class Goat extends Animal {
|
|
return InstrumentItem.create(Items.GOAT_HORN, (Holder) holderset.getRandomElement(randomsource).get());
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.goatRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.goatRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.goatControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.goatBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.goatTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.goatAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected Brain.Provider<Goat> brainProvider() {
|
|
return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES);
|
|
@@ -191,13 +223,14 @@ public class Goat extends Animal {
|
|
private int behaviorTick = 0; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("goatBrain");
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ //this.level.getProfiler().push("goatBrain"); // Purpur
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick((ServerLevel) this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
- this.level.getProfiler().push("goatActivityUpdate");
|
|
+ //this.level.getProfiler().pop
|
|
+ // (); // Purpur
|
|
+ //this.level.getProfiler().push("goatActivityUpdate"); // Purpur
|
|
GoatAi.updateActivity(this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
super.customServerAiStep();
|
|
}
|
|
|
|
@@ -389,6 +422,7 @@ public class Goat extends Animal {
|
|
|
|
// Paper start - Goat ram API
|
|
public void ram(net.minecraft.world.entity.LivingEntity entity) {
|
|
+ if(!new org.purpurmc.purpur.event.entity.GoatRamEntityEvent((org.bukkit.entity.Goat) getBukkitEntity(), (org.bukkit.entity.LivingEntity) entity.getBukkitLivingEntity()).callEvent()) return; // Purpur
|
|
Brain<Goat> brain = this.getBrain();
|
|
brain.setMemory(MemoryModuleType.RAM_TARGET, entity.position());
|
|
brain.eraseMemory(MemoryModuleType.RAM_COOLDOWN_TICKS);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
|
|
index 47cd69f91bbc2e2be9ec970674adc522e21593c8..c044ed3a96f10584fd5aec836624bca1b414182d 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
|
|
@@ -144,12 +144,60 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
|
|
|
|
protected AbstractHorse(EntityType<? extends AbstractHorse> type, Level world) {
|
|
super(type, world);
|
|
+ this.moveControl = new net.minecraft.world.entity.ai.control.MoveControl(this); // Purpur - use vanilla controller
|
|
+ this.lookControl = new net.minecraft.world.entity.ai.control.LookControl(this); // Purpur - use vanilla controller
|
|
this.setMaxUpStep(1.0F);
|
|
this.createInventory();
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return false; // vanilla handles
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.generateMaxHealth(random));
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.generateSpeed(random));
|
|
+ this.getAttribute(Attributes.JUMP_STRENGTH).setBaseValue(this.generateJumpStrength(random));
|
|
+ }
|
|
+
|
|
+ protected double generateMaxHealth(double min, double max) {
|
|
+ if (min == max) return min;
|
|
+ int diff = Mth.floor(max - min);
|
|
+ double base = max - diff;
|
|
+ int first = Mth.floor((double) diff / 2);
|
|
+ int rest = diff - first;
|
|
+ return base + random.nextInt(first + 1) + random.nextInt(rest + 1);
|
|
+ }
|
|
+
|
|
+ protected double generateJumpStrength(double min, double max) {
|
|
+ if (min == max) return min;
|
|
+ return min + (max - min) * this.random.nextDouble();
|
|
+ }
|
|
+
|
|
+ protected double generateSpeed(double min, double max) {
|
|
+ if (min == max) return min;
|
|
+ return min + (max - min) * this.random.nextDouble();
|
|
+ }
|
|
+
|
|
+ protected float generateMaxHealth(RandomSource random) {
|
|
+ return 15.0F + (float) random.nextInt(8) + (float) random.nextInt(9);
|
|
+ }
|
|
+
|
|
+ protected double generateJumpStrength(RandomSource random) {
|
|
+ return 0.4000000059604645D + random.nextDouble() * 0.2D + random.nextDouble() * 0.2D + random.nextDouble() * 0.2D;
|
|
+ }
|
|
+
|
|
+ protected double generateSpeed(RandomSource random) {
|
|
+ return (0.44999998807907104D + random.nextDouble() * 0.3D + random.nextDouble() * 0.3D + random.nextDouble() * 0.3D) * 0.25D;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D, AbstractHorse.class));
|
|
@@ -160,6 +208,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
|
|
if (this.canPerformRearing()) {
|
|
this.goalSelector.addGoal(9, new RandomStandGoal(this));
|
|
}
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur
|
|
|
|
this.addBehaviourGoals();
|
|
}
|
|
@@ -336,7 +385,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
|
|
|
|
@Override
|
|
protected int calculateFallDamage(float fallDistance, float damageMultiplier) {
|
|
- return Mth.ceil((fallDistance * 0.5F - 3.0F) * damageMultiplier);
|
|
+ return Mth.ceil((fallDistance * 0.5F - this.safeFallDistance) * damageMultiplier);
|
|
}
|
|
|
|
protected int getInventorySize() {
|
|
@@ -1252,7 +1301,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
|
|
entityData = new AgeableMob.AgeableMobGroupData(0.2F);
|
|
}
|
|
|
|
- this.randomizeAttributes(world.getRandom());
|
|
+ // this.randomizeAttributes(world.getRandom()); // Purpur - replaced by initAttributes()
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData, entityNbt);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
|
|
index e0dfee0e0ce091d5ae0ec740e939c2c50915c104..7ad29aacc73ca1cb98b76ad36b92a3edb2256629 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
|
|
@@ -15,6 +15,43 @@ public class Donkey extends AbstractChestedHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.donkeyRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.donkeyMaxHealthMin, this.level.purpurConfig.donkeyMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.donkeyJumpStrengthMin, this.level.purpurConfig.donkeyJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.donkeyMovementSpeedMin, this.level.purpurConfig.donkeyMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.donkeyBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.donkeyTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.donkeyAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.DONKEY_AMBIENT;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
|
|
index 79a2b3c8df70a9a73ad44560a4a6129f91db8e16..fb433878731b824b4d595b7f28626f25bdfabbeb 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
|
|
@@ -40,6 +40,43 @@ public class Horse extends AbstractHorse implements VariantHolder<Variant> {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.horseRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.horseMaxHealthMin, this.level.purpurConfig.horseMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.horseJumpStrengthMin, this.level.purpurConfig.horseJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.horseMovementSpeedMin, this.level.purpurConfig.horseMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.horseBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.horseTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.horseAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void randomizeAttributes(RandomSource random) {
|
|
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
|
|
index 7ae0e4b3aa8e861500ddc7b38aa671258b532fcd..309fd5bccadcc584354d328bd31a6f4591c2d0a0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
|
|
@@ -73,11 +73,86 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
private Llama caravanHead;
|
|
@Nullable
|
|
public Llama caravanTail; // Paper
|
|
+ public boolean shouldJoinCaravan = true; // Purpur
|
|
|
|
public Llama(EntityType<? extends Llama> type, Level world) {
|
|
super(type, world);
|
|
+ // Purpur start
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.llamaRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.llamaRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.llamaControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSaddled() {
|
|
+ return super.isSaddled() || (isTamed() && getSwag() != null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.llamaMaxHealthMin, this.level.purpurConfig.llamaMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.llamaJumpStrengthMin, this.level.purpurConfig.llamaJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.llamaMovementSpeedMin, this.level.purpurConfig.llamaMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.llamaBreedingTicks;
|
|
}
|
|
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.llamaTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.llamaAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public boolean isTraderLlama() {
|
|
return false;
|
|
}
|
|
@@ -110,7 +185,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
if (!this.inventory.getItem(1).isEmpty()) {
|
|
nbt.put("DecorItem", this.inventory.getItem(1).save(new CompoundTag()));
|
|
}
|
|
-
|
|
+ nbt.putBoolean("Purpur.ShouldJoinCaravan", shouldJoinCaravan); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -121,13 +196,14 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
if (nbt.contains("DecorItem", 10)) {
|
|
this.inventory.setItem(1, ItemStack.of(nbt.getCompound("DecorItem")));
|
|
}
|
|
-
|
|
+ if (nbt.contains("Purpur.ShouldJoinCaravan")) this.shouldJoinCaravan = nbt.getBoolean("Purpur.ShouldJoinCaravan"); // Purpur
|
|
this.updateContainerEquipment();
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D));
|
|
this.goalSelector.addGoal(2, new LlamaFollowCaravanGoal(this, 2.0999999046325684D));
|
|
this.goalSelector.addGoal(3, new RangedAttackGoal(this, 1.25D, 40, 20.0F));
|
|
@@ -138,6 +214,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.7D));
|
|
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new Llama.LlamaHurtByTargetGoal(this));
|
|
this.targetSelector.addGoal(2, new Llama.LlamaAttackWolfGoal(this));
|
|
}
|
|
@@ -460,6 +537,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
|
|
public void leaveCaravan() {
|
|
if (this.caravanHead != null) {
|
|
+ new org.purpurmc.purpur.event.entity.LlamaLeaveCaravanEvent((org.bukkit.entity.Llama) getBukkitEntity()).callEvent(); // Purpur
|
|
this.caravanHead.caravanTail = null;
|
|
}
|
|
|
|
@@ -467,6 +545,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
|
|
}
|
|
|
|
public void joinCaravan(Llama llama) {
|
|
+ if (!this.level.purpurConfig.llamaJoinCaravans || !shouldJoinCaravan || !new org.purpurmc.purpur.event.entity.LlamaJoinCaravanEvent((org.bukkit.entity.Llama) getBukkitEntity(), (org.bukkit.entity.Llama) llama.getBukkitEntity()).callEvent()) return; // Purpur
|
|
this.caravanHead = llama;
|
|
this.caravanHead.caravanTail = this;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
|
|
index a6601f70890f90691923c0e6a9f10ea597ccabc2..59f1acea8990ad4e9d3a71f6f1c790c4356169f0 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
|
|
@@ -14,6 +14,43 @@ public class Mule extends AbstractChestedHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.muleRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.muleMaxHealthMin, this.level.purpurConfig.muleMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.muleJumpStrengthMin, this.level.purpurConfig.muleJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.muleMovementSpeedMin, this.level.purpurConfig.muleMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.muleBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.muleTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.muleAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.MULE_AMBIENT;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
|
|
index c952190181ad6ea090c4b91c3bf51db45ed3e5ae..ac3f8e004445dde937625ecaad7d51a3576e37d3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
|
|
@@ -28,6 +28,43 @@ public class SkeletonHorse extends AbstractHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isTamed() {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.skeletonHorseMaxHealthMin, this.level.purpurConfig.skeletonHorseMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.skeletonHorseJumpStrengthMin, this.level.purpurConfig.skeletonHorseJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.skeletonHorseMovementSpeedMin, this.level.purpurConfig.skeletonHorseMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return 6000;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.skeletonHorseTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.skeletonHorseAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0D).add(Attributes.MOVEMENT_SPEED, (double)0.2F);
|
|
}
|
|
@@ -39,6 +76,7 @@ public class SkeletonHorse extends AbstractHorse {
|
|
|
|
@Override
|
|
protected void addBehaviourGoals() {
|
|
+ if (level.purpurConfig.skeletonHorseCanSwim) goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this));
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
|
|
index 691f85a508c0b79b95b88c7e3118f02ec92a5123..834ab8bc597a5fb195e94454c0f9b81cf4c7e0f3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
|
|
@@ -27,6 +27,58 @@ public class TraderLlama extends Llama {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.traderLlamaRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.traderLlamaRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.traderLlamaControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSaddled() {
|
|
+ return super.isSaddled() || isTamed();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(net.minecraft.util.RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.traderLlamaMaxHealthMin, this.level.purpurConfig.traderLlamaMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(net.minecraft.util.RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.traderLlamaJumpStrengthMin, this.level.purpurConfig.traderLlamaJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(net.minecraft.util.RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.traderLlamaMovementSpeedMin, this.level.purpurConfig.traderLlamaMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.traderLlamaBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.traderLlamaTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.traderLlamaAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean isTraderLlama() {
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
|
|
index e2935115c8d41af1d623da4f0d4f73de80386129..ae663a9b32c281f745592afa614f55c0280b4c58 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
|
|
@@ -21,6 +21,48 @@ public class ZombieHorse extends AbstractHorse {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.zombieHorseRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isTamed() {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float generateMaxHealth(RandomSource random) {
|
|
+ return (float) generateMaxHealth(this.level.purpurConfig.zombieHorseMaxHealthMin, this.level.purpurConfig.zombieHorseMaxHealthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateJumpStrength(RandomSource random) {
|
|
+ return generateJumpStrength(this.level.purpurConfig.zombieHorseJumpStrengthMin, this.level.purpurConfig.zombieHorseJumpStrengthMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double generateSpeed(RandomSource random) {
|
|
+ return generateSpeed(this.level.purpurConfig.zombieHorseMovementSpeedMin, this.level.purpurConfig.zombieHorseMovementSpeedMax);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return 6000;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.zombieHorseTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.zombieHorseAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0D).add(Attributes.MOVEMENT_SPEED, (double)0.2F);
|
|
}
|
|
@@ -63,5 +105,6 @@ public class ZombieHorse extends AbstractHorse {
|
|
|
|
@Override
|
|
protected void addBehaviourGoals() {
|
|
+ if (level.purpurConfig.zombieHorseCanSwim) goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
|
|
index f6e2348b280eaefc0eb05bf5d962593caa654357..adae992ade60e0fce7ca0cc10192720025a574fe 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
|
|
@@ -81,6 +81,33 @@ public class Sniffer extends Animal {
|
|
this.setPathfindingMalus(BlockPathTypes.WATER, -2.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.snifferRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.snifferRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.snifferControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.snifferMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.snifferBreedingTicks;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// CraftBukkit start - SPIGOT-7295: moved from constructor to appropriate location
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
@@ -303,7 +330,7 @@ public class Sniffer extends Animal {
|
|
}
|
|
|
|
@Override
|
|
- protected void jumpFromGround() {
|
|
+ public void jumpFromGround() { // Purpur - protected -> public
|
|
super.jumpFromGround();
|
|
double d0 = this.moveControl.getSpeedModifier();
|
|
|
|
@@ -443,11 +470,11 @@ public class Sniffer extends Animal {
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("snifferBrain");
|
|
+ //this.level.getProfiler().push("snifferBrain"); // Purpur
|
|
this.getBrain().tick((ServerLevel) this.level, this);
|
|
- this.level.getProfiler().popPush("snifferActivityUpdate");
|
|
+ //this.level.getProfiler().popPush("snifferActivityUpdate"); // Purpur
|
|
SnifferAi.updateActivity(this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
super.customServerAiStep();
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
|
|
index de84a00ce2d2b7c654b08164489624e124568346..998c72513df1dcd2b1316b320b3d5e7ca8e69fd4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
|
|
@@ -24,6 +24,13 @@ public class EnderDragonPart extends Entity {
|
|
this.name = name;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) {
|
|
+ return parentMob.isAlive() ? parentMob.tryRide(player, hand) : net.minecraft.world.InteractionResult.PASS;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
|
|
index 64f17b4a22454b59968787089253eaba0a04c1f2..e3fe5f18c77e36479eaeb7edfd2a3eb919c342d6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
|
|
@@ -30,6 +30,12 @@ public class EndCrystal extends Entity {
|
|
private static final EntityDataAccessor<Boolean> DATA_SHOW_BOTTOM = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.BOOLEAN);
|
|
public int time;
|
|
public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals
|
|
+ // Purpur start
|
|
+ private net.minecraft.world.entity.monster.Phantom targetPhantom;
|
|
+ private int phantomBeamTicks = 0;
|
|
+ private int phantomDamageCooldown = 0;
|
|
+ private int idleCooldown = 0;
|
|
+ // Purpur end
|
|
|
|
public EndCrystal(EntityType<? extends EndCrystal> type, Level world) {
|
|
super(type, world);
|
|
@@ -77,9 +83,69 @@ public class EndCrystal extends Entity {
|
|
}
|
|
}
|
|
// Paper end
|
|
+ if (this.level.purpurConfig.endCrystalCramming > 0 && this.level.getEntitiesOfClass(EndCrystal.class, getBoundingBox()).size() > this.level.purpurConfig.endCrystalCramming) this.hurt(this.damageSources().cramming(), 6.0F); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.phantomAttackedByCrystalRadius <= 0 || --idleCooldown > 0) {
|
|
+ return; // on cooldown
|
|
+ }
|
|
+
|
|
+ if (targetPhantom == null) {
|
|
+ for (net.minecraft.world.entity.monster.Phantom phantom : level.getEntitiesOfClass(net.minecraft.world.entity.monster.Phantom.class, getBoundingBox().inflate(level.purpurConfig.phantomAttackedByCrystalRadius))) {
|
|
+ if (phantom.hasLineOfSight(this)) {
|
|
+ attackPhantom(phantom);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ setBeamTarget(new BlockPos(targetPhantom).offset(0, -2, 0));
|
|
+ if (--phantomBeamTicks > 0 && targetPhantom.isAlive()) {
|
|
+ phantomDamageCooldown--;
|
|
+ if (targetPhantom.hasLineOfSight(this)) {
|
|
+ if (phantomDamageCooldown <= 0) {
|
|
+ phantomDamageCooldown = 20;
|
|
+ targetPhantom.hurt(targetPhantom.damageSources().indirectMagic(this, this), level.purpurConfig.phantomAttackedByCrystalDamage);
|
|
+ }
|
|
+ } else {
|
|
+ forgetPhantom(); // no longer in sight
|
|
+ }
|
|
+ } else {
|
|
+ forgetPhantom(); // attacked long enough
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void attackPhantom(net.minecraft.world.entity.monster.Phantom phantom) {
|
|
+ phantomDamageCooldown = 0;
|
|
+ phantomBeamTicks = 60;
|
|
+ targetPhantom = phantom;
|
|
+ }
|
|
+
|
|
+ private void forgetPhantom() {
|
|
+ targetPhantom = null;
|
|
+ setBeamTarget(null);
|
|
+ phantomBeamTicks = 0;
|
|
+ phantomDamageCooldown = 0;
|
|
+ idleCooldown = 60;
|
|
+ }
|
|
+
|
|
+ public boolean shouldExplode() {
|
|
+ return showsBottom() ? level.purpurConfig.basedEndCrystalExplode : level.purpurConfig.baselessEndCrystalExplode;
|
|
+ }
|
|
+
|
|
+ public float getExplosionPower() {
|
|
+ return (float) (showsBottom() ? level.purpurConfig.basedEndCrystalExplosionPower : level.purpurConfig.baselessEndCrystalExplosionPower);
|
|
+ }
|
|
+
|
|
+ public boolean hasExplosionFire() {
|
|
+ return showsBottom() ? level.purpurConfig.basedEndCrystalExplosionFire : level.purpurConfig.baselessEndCrystalExplosionFire;
|
|
+ }
|
|
+
|
|
+ public Level.ExplosionInteraction getExplosionEffect() {
|
|
+ return showsBottom() ? level.purpurConfig.basedEndCrystalExplosionEffect : level.purpurConfig.baselessEndCrystalExplosionEffect;
|
|
}
|
|
+ // Purpur end
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(CompoundTag nbt) {
|
|
@@ -124,17 +190,19 @@ public class EndCrystal extends Entity {
|
|
// CraftBukkit end
|
|
this.remove(Entity.RemovalReason.KILLED);
|
|
if (!source.is(DamageTypeTags.IS_EXPLOSION)) {
|
|
+ if (shouldExplode()) {// Purpur
|
|
DamageSource damagesource1 = source.getEntity() != null ? this.damageSources().explosion(this, source.getEntity()) : null;
|
|
|
|
// CraftBukkit start
|
|
- ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 6.0F, false);
|
|
+ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), getExplosionPower(), hasExplosionFire()); // Purpur
|
|
this.level.getCraftServer().getPluginManager().callEvent(event);
|
|
if (event.isCancelled()) {
|
|
this.unsetRemoved();
|
|
return false;
|
|
}
|
|
- this.level.explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.BLOCK);
|
|
+ this.level.explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), getExplosionEffect()); // Purpur
|
|
// CraftBukkit end
|
|
+ } else this.unsetRemoved(); // Purpur
|
|
}
|
|
|
|
this.onDestroyedBy(source);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
|
index 3f66986948d0b43a75454389b7ec8517e2d50899..b6ac41633e91f6ee2755d1f05aac4c8046a4aa8a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
|
@@ -104,9 +104,11 @@ public class EnderDragon extends Mob implements Enemy {
|
|
@Nullable
|
|
private BlockPos podium;
|
|
// Paper end
|
|
+ private boolean hadRider; // Purpur
|
|
|
|
public EnderDragon(EntityType<? extends EnderDragon> entitytypes, Level world) {
|
|
super(EntityType.ENDER_DRAGON, world);
|
|
+ this.explosionSource = new Explosion(world, this, null, null, Double.NaN, Double.NaN, Double.NaN, Float.NaN, true, Explosion.BlockInteraction.DESTROY); // Purpur - moved instantiation from field
|
|
this.subEntities = new EnderDragonPart[]{this.head, this.neck, this.body, this.tail1, this.tail2, this.tail3, this.wing1, this.wing2};
|
|
this.setHealth(this.getMaxHealth());
|
|
this.noPhysics = true;
|
|
@@ -118,8 +120,59 @@ public class EnderDragon extends Mob implements Enemy {
|
|
}
|
|
|
|
this.phaseManager = new EnderDragonPhaseManager(this);
|
|
- this.explosionSource = new Explosion(world, this, null, null, Double.NaN, Double.NaN, Double.NaN, Float.NaN, true, Explosion.BlockInteraction.DESTROY); // CraftBukkit
|
|
+
|
|
+ // Purpur start
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this) {
|
|
+ @Override
|
|
+ public void vanillaTick() {
|
|
+ // dragon doesn't use the controller. do nothing
|
|
+ }
|
|
+ };
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
|
|
+ @Override
|
|
+ public void vanillaTick() {
|
|
+ // dragon doesn't use the controller. do nothing
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ setYawPitch(rider.getYRot() - 180F, rider.xRotO * 0.5F);
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.enderDragonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.enderDragonRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.enderDragonControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.enderDragonMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.enderDragonMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.enderDragonTakeDamageFromWater;
|
|
}
|
|
+ // Purpur end
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0D);
|
|
@@ -182,6 +235,37 @@ public class EnderDragon extends Mob implements Enemy {
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
+ // Purpur start
|
|
+ boolean hasRider = getRider() != null && this.isControllable();
|
|
+ if (hasRider) {
|
|
+ if (!hadRider) {
|
|
+ hadRider = true;
|
|
+ noPhysics = false;
|
|
+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(4.0F, 2.0F);
|
|
+ }
|
|
+
|
|
+ // dragon doesn't use controllers, so must tick manually
|
|
+ moveControl.tick();
|
|
+ lookControl.tick();
|
|
+
|
|
+ moveRelative((float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F, new Vec3(-getStrafeMot(), getVerticalMot(), -getForwardMot()));
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot);
|
|
+ move(MoverType.PLAYER, mot);
|
|
+
|
|
+ mot = mot.multiply(0.9F, 0.9F, 0.9F);
|
|
+ setDeltaMovement(mot);
|
|
+
|
|
+ // control wing flap speed on client
|
|
+ phaseManager.setPhase(mot.x() * mot.x() + mot.z() * mot.z() < 0.005F ? EnderDragonPhase.HOVERING : EnderDragonPhase.HOLDING_PATTERN);
|
|
+ } else if (hadRider) {
|
|
+ hadRider = false;
|
|
+ noPhysics = true;
|
|
+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(16.0F, 8.0F);
|
|
+ phaseManager.setPhase(EnderDragonPhase.HOLDING_PATTERN); // HoldingPattern
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.processFlappingMovement();
|
|
if (this.level.isClientSide) {
|
|
this.setHealth(this.getHealth());
|
|
@@ -195,6 +279,8 @@ public class EnderDragon extends Mob implements Enemy {
|
|
float f;
|
|
|
|
if (this.isDeadOrDying()) {
|
|
+ if (hasRider) ejectPassengers(); // Purpur
|
|
+
|
|
float f1 = (this.random.nextFloat() - 0.5F) * 8.0F;
|
|
|
|
f = (this.random.nextFloat() - 0.5F) * 4.0F;
|
|
@@ -207,9 +293,9 @@ public class EnderDragon extends Mob implements Enemy {
|
|
|
|
f = 0.2F / ((float) vec3d.horizontalDistance() * 10.0F + 1.0F);
|
|
f *= (float) Math.pow(2.0D, vec3d.y);
|
|
- if (this.phaseManager.getCurrentPhase().isSitting()) {
|
|
+ if (!hasRider && this.phaseManager.getCurrentPhase().isSitting()) { // Purpur
|
|
this.flapTime += 0.1F;
|
|
- } else if (this.inWall) {
|
|
+ } else if (!hasRider && this.inWall) { // Purpur
|
|
this.flapTime += f * 0.5F;
|
|
} else {
|
|
this.flapTime += f;
|
|
@@ -254,7 +340,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
}
|
|
|
|
this.phaseManager.getCurrentPhase().doClientTick();
|
|
- } else {
|
|
+ } else if (!hasRider) { // Purpur
|
|
DragonPhaseInstance idragoncontroller = this.phaseManager.getCurrentPhase();
|
|
|
|
idragoncontroller.doServerTick();
|
|
@@ -323,7 +409,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
this.tickPart(this.body, (double) (f11 * 0.5F), 0.0D, (double) (-f12 * 0.5F));
|
|
this.tickPart(this.wing1, (double) (f12 * 4.5F), 2.0D, (double) (f11 * 4.5F));
|
|
this.tickPart(this.wing2, (double) (f12 * -4.5F), 2.0D, (double) (f11 * -4.5F));
|
|
- if (!this.level.isClientSide && this.hurtTime == 0) {
|
|
+ if (!hasRider && !this.level.isClientSide && this.hurtTime == 0) { // Purpur
|
|
this.knockBack(this.level.getEntities((Entity) this, this.wing1.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
|
|
this.knockBack(this.level.getEntities((Entity) this, this.wing2.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
|
|
this.hurt(this.level.getEntities((Entity) this, this.head.getBoundingBox().inflate(1.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
|
|
@@ -367,7 +453,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
}
|
|
|
|
if (!this.level.isClientSide) {
|
|
- this.inWall = this.checkWalls(this.head.getBoundingBox()) | this.checkWalls(this.neck.getBoundingBox()) | this.checkWalls(this.body.getBoundingBox());
|
|
+ this.inWall = !hasRider && this.checkWalls(this.head.getBoundingBox()) | this.checkWalls(this.neck.getBoundingBox()) | this.checkWalls(this.body.getBoundingBox()); // Purpur
|
|
if (this.dragonFight != null) {
|
|
this.dragonFight.updateDragon(this);
|
|
}
|
|
@@ -499,7 +585,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
BlockState iblockdata = this.level.getBlockState(blockposition);
|
|
|
|
if (!iblockdata.isAir() && !iblockdata.is(BlockTags.DRAGON_TRANSPARENT)) {
|
|
- if (this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) {
|
|
+ if ((this.level.purpurConfig.enderDragonBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) { // Purpur
|
|
// CraftBukkit start - Add blocks to list rather than destroying them
|
|
// flag1 = this.level.removeBlock(blockposition, false) || flag1;
|
|
flag1 = true;
|
|
@@ -634,7 +720,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT);
|
|
short short0 = 500;
|
|
|
|
- if (this.dragonFight != null && !this.dragonFight.hasPreviouslyKilledDragon()) {
|
|
+ if (this.dragonFight != null && (level.purpurConfig.enderDragonAlwaysDropsFullExp || !this.dragonFight.hasPreviouslyKilledDragon())) {
|
|
short0 = 12000;
|
|
}
|
|
|
|
@@ -1069,6 +1155,7 @@ public class EnderDragon extends Mob implements Enemy {
|
|
|
|
@Override
|
|
protected boolean canRide(Entity entity) {
|
|
+ if (this.level.purpurConfig.enderDragonCanRideVehicles) return this.boardingCooldown <= 0; // Purpur
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
|
|
index e81e8f050bd9df34b6a64c741428503b434f03a3..4781bdd3b6c7d6b686f2fe6af530e82861385342 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
|
|
@@ -84,16 +84,31 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
return entityliving.getMobType() != MobType.UNDEAD && entityliving.attackable();
|
|
};
|
|
private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR);
|
|
+ private int shootCooldown = 0; // Purpur
|
|
+ @Nullable private java.util.UUID summoner; // Purpur
|
|
// Paper start
|
|
private boolean canPortal = false;
|
|
|
|
public void setCanTravelThroughPortals(boolean canPortal) { this.canPortal = canPortal; }
|
|
// Paper end
|
|
+ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur
|
|
|
|
public WitherBoss(EntityType<? extends WitherBoss> type, Level world) {
|
|
super(type, world);
|
|
this.bossEvent = (ServerBossEvent) (new ServerBossEvent(this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS)).setDarkenScreen(true);
|
|
- this.moveControl = new FlyingMoveControl(this, 10, false);
|
|
+ // Purpur start
|
|
+ this.purpurController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.1F);
|
|
+ this.moveControl = new FlyingMoveControl(this, 10, false) {
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (mob.getRider() != null && mob.isControllable()) {
|
|
+ purpurController.purpurTick(mob.getRider());
|
|
+ } else {
|
|
+ super.tick();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.setHealth(this.getMaxHealth());
|
|
this.xpReward = 50;
|
|
}
|
|
@@ -108,13 +123,148 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
return navigationflying;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.witherRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.witherRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.witherControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.witherMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 5F;
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.5, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ this.entityData.set(DATA_TARGETS.get(0), 0);
|
|
+ this.entityData.set(DATA_TARGETS.get(1), 0);
|
|
+ this.entityData.set(DATA_TARGETS.get(2), 0);
|
|
+ getNavigation().stop();
|
|
+ shootCooldown = 20;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onClick(net.minecraft.world.InteractionHand hand) {
|
|
+ return shoot(getRider(), hand == net.minecraft.world.InteractionHand.MAIN_HAND ? new int[]{1} : new int[]{2});
|
|
+ }
|
|
+
|
|
+ public boolean shoot(@Nullable Player rider, int[] heads) {
|
|
+ if (shootCooldown > 0) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ shootCooldown = 20;
|
|
+ if (rider == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity player = rider.getBukkitEntity();
|
|
+ if (!player.hasPermission("allow.special.wither")) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ net.minecraft.world.phys.HitResult rayTrace = getRayTrace(120, net.minecraft.world.level.ClipContext.Fluid.NONE);
|
|
+ if (rayTrace == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ Vec3 loc;
|
|
+ if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) {
|
|
+ BlockPos pos = ((net.minecraft.world.phys.BlockHitResult) rayTrace).getBlockPos();
|
|
+ loc = new Vec3(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D);
|
|
+ } else if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.ENTITY) {
|
|
+ Entity target = ((net.minecraft.world.phys.EntityHitResult) rayTrace).getEntity();
|
|
+ loc = new Vec3(target.getX(), target.getY() + (target.getEyeHeight() / 2), target.getZ());
|
|
+ } else {
|
|
+ org.bukkit.block.Block block = player.getTargetBlock(null, 120);
|
|
+ loc = new Vec3(block.getX() + 0.5D, block.getY() + 0.5D, block.getZ() + 0.5D);
|
|
+ }
|
|
+
|
|
+ for (int head : heads) {
|
|
+ shoot(head, loc.x(), loc.y(), loc.z(), rider);
|
|
+ }
|
|
+
|
|
+ return true; // handled
|
|
+ }
|
|
+
|
|
+ public void shoot(int head, double x, double y, double z, Player rider) {
|
|
+ level.levelEvent(null, 1024, blockPosition(), 0);
|
|
+ double headX = getHeadX(head);
|
|
+ double headY = getHeadY(head);
|
|
+ double headZ = getHeadZ(head);
|
|
+ WitherSkull skull = new WitherSkull(level, this, x - headX, y - headY, z - headZ) {
|
|
+ @Override
|
|
+ public boolean canHitEntity(Entity target) {
|
|
+ // do not hit rider
|
|
+ return target != rider && super.canHitEntity(target);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canSaveToDisk() {
|
|
+ return false;
|
|
+ }
|
|
+ };
|
|
+ skull.setPosRaw(headX, headY, headZ);
|
|
+ level.addFreshEntity(skull);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.witherMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.witherTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return summoner;
|
|
+ }
|
|
+
|
|
+ public void setSummoner(@Nullable java.util.UUID summoner) {
|
|
+ this.summoner = summoner;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.witherAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(0, new WitherBoss.WitherDoNothingGoal());
|
|
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 40, 20.0F));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomFlyingGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 0, false, false, WitherBoss.LIVING_ENTITY_SELECTOR));
|
|
}
|
|
@@ -132,6 +282,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putInt("Invul", this.getInvulnerableTicks());
|
|
+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -141,6 +292,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
if (this.hasCustomName()) {
|
|
this.bossEvent.setName(this.getDisplayName());
|
|
}
|
|
+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur
|
|
|
|
}
|
|
|
|
@@ -256,6 +408,16 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
|
|
+ }
|
|
+ if (shootCooldown > 0) {
|
|
+ shootCooldown--;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
int i;
|
|
|
|
if (this.getInvulnerableTicks() > 0) {
|
|
@@ -272,7 +434,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
}
|
|
// CraftBukkit end
|
|
|
|
- if (!this.isSilent()) {
|
|
+ if (!this.isSilent() && level.purpurConfig.witherPlaySpawnSound) {
|
|
// CraftBukkit start - Use relative location for far away sounds
|
|
// this.world.globalLevelEvent(1023, new BlockPosition(this), 0);
|
|
int viewDistance = ((ServerLevel) this.level).getCraftServer().getViewDistance() * 16;
|
|
@@ -296,7 +458,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
|
|
this.setInvulnerableTicks(i);
|
|
if (this.tickCount % 10 == 0) {
|
|
- this.heal(10.0F, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit
|
|
+ this.heal(this.getMaxHealth() / 33, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit // Purpur
|
|
}
|
|
|
|
} else {
|
|
@@ -356,7 +518,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
|
|
if (this.destroyBlocksTick > 0) {
|
|
--this.destroyBlocksTick;
|
|
- if (this.destroyBlocksTick == 0 && this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (this.destroyBlocksTick == 0 && (this.level.purpurConfig.witherBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur
|
|
i = Mth.floor(this.getY());
|
|
j = Mth.floor(this.getX());
|
|
int i1 = Mth.floor(this.getZ());
|
|
@@ -389,8 +551,10 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
}
|
|
}
|
|
|
|
- if (this.tickCount % 20 == 0) {
|
|
- this.heal(1.0F, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit
|
|
+ // Purpur start - customizable heal rate and amount
|
|
+ if (this.tickCount % level.purpurConfig.witherHealthRegenDelay == 0) {
|
|
+ this.heal(level.purpurConfig.witherHealthRegenAmount, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit
|
|
+ // Purpur end
|
|
}
|
|
|
|
this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth());
|
|
@@ -576,11 +740,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
}
|
|
|
|
public int getAlternativeTarget(int headIndex) {
|
|
- return (Integer) this.entityData.get((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex));
|
|
+ return getRider() != null && this.isControllable() ? 0 : this.entityData.get(WitherBoss.DATA_TARGETS.get(headIndex)); // Purpur
|
|
}
|
|
|
|
public void setAlternativeTarget(int headIndex, int id) {
|
|
- this.entityData.set((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex), id);
|
|
+ if (getRider() == null || !this.isControllable()) this.entityData.set(WitherBoss.DATA_TARGETS.get(headIndex), id); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -595,6 +759,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
|
|
@Override
|
|
protected boolean canRide(Entity entity) {
|
|
+ if (this.level.purpurConfig.witherCanRideVehicles) return this.boardingCooldown <= 0; // Purpur
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
index 3677dd991ae73428984e62e4d6fb757317987887..0545a39af0f21210ff1f5e53f6d712ae24ce43e4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
@@ -99,10 +99,12 @@ public class ArmorStand extends LivingEntity {
|
|
private boolean noTickPoseDirty = false;
|
|
private boolean noTickEquipmentDirty = false;
|
|
// Paper end
|
|
+ public boolean canMovementTick = true; // Purpur
|
|
|
|
public ArmorStand(EntityType<? extends ArmorStand> type, Level world) {
|
|
super(type, world);
|
|
if (world != null) this.canTick = world.paperConfig().entities.armorStands.tick; // Paper - armour stand ticking
|
|
+ if (world != null) this.canMovementTick = world.purpurConfig.armorstandMovement; // Purpur
|
|
this.handItems = NonNullList.withSize(2, ItemStack.EMPTY);
|
|
this.armorItems = NonNullList.withSize(4, ItemStack.EMPTY);
|
|
this.headPose = ArmorStand.DEFAULT_HEAD_POSE;
|
|
@@ -112,6 +114,7 @@ public class ArmorStand extends LivingEntity {
|
|
this.leftLegPose = ArmorStand.DEFAULT_LEFT_LEG_POSE;
|
|
this.rightLegPose = ArmorStand.DEFAULT_RIGHT_LEG_POSE;
|
|
this.setMaxUpStep(0.0F);
|
|
+ this.setShowArms(world != null && world.purpurConfig.armorstandPlaceWithArms); // Purpur
|
|
}
|
|
|
|
public ArmorStand(Level world, double x, double y, double z) {
|
|
@@ -609,7 +612,7 @@ public class ArmorStand extends LivingEntity {
|
|
private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(DamageSource damageSource) { // Paper
|
|
ItemStack itemstack = new ItemStack(Items.ARMOR_STAND);
|
|
|
|
- if (this.hasCustomName()) {
|
|
+ if (this.level.purpurConfig.persistentDroppableEntityDisplayNames && this.hasCustomName()) { // Purpur
|
|
itemstack.setHoverName(this.getCustomName());
|
|
}
|
|
|
|
@@ -685,6 +688,7 @@ public class ArmorStand extends LivingEntity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ maxUpStep = level.purpurConfig.armorstandStepHeight;
|
|
// Paper start
|
|
if (!this.canTick) {
|
|
if (this.noTickPoseDirty) {
|
|
@@ -1006,4 +1010,18 @@ public class ArmorStand extends LivingEntity {
|
|
}
|
|
// Paper end
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void updateInWaterStateAndDoWaterCurrentPushing() {
|
|
+ if (this.level.purpurConfig.armorstandWaterMovement &&
|
|
+ (this.level.purpurConfig.armorstandWaterFence || !(level.getBlockState(blockPosition().below()).getBlock() instanceof net.minecraft.world.level.block.FenceBlock)))
|
|
+ super.updateInWaterStateAndDoWaterCurrentPushing();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void aiStep() {
|
|
+ if (this.canMovementTick && this.canMove) super.aiStep();
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
|
index 30aec9dff249ae629b22318e52902361a9fa4099..25158e04c39146218b21ce5d5c963a24be68b2e2 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
|
@@ -272,7 +272,13 @@ public class ItemFrame extends HangingEntity {
|
|
}
|
|
|
|
if (alwaysDrop) {
|
|
- this.spawnAtLocation(this.getFrameItemStack());
|
|
+ // Purpur start
|
|
+ final ItemStack itemFrame = this.getFrameItemStack();
|
|
+ if (this.level.purpurConfig.persistentDroppableEntityDisplayNames && this.hasCustomName()) {
|
|
+ itemFrame.setHoverName(this.getCustomName());
|
|
+ }
|
|
+ this.spawnAtLocation(itemFrame);
|
|
+ // Purpur end
|
|
}
|
|
|
|
if (!itemstack.isEmpty()) {
|
|
@@ -287,6 +293,13 @@ public class ItemFrame extends HangingEntity {
|
|
}
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Nullable
|
|
+ public net.minecraft.world.entity.item.ItemEntity spawnAtLocation(ItemStack stack) {
|
|
+ return this.spawnAtLocation(stack, getDirection().equals(Direction.DOWN) ? -0.6F : 0.0F);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
private void removeFramedMap(ItemStack itemstack) {
|
|
// Paper start - fix MC-252817 (green map markers do not disappear)
|
|
this.getFramedMapIdFromItem(itemstack).ifPresent((i) -> {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/decoration/Painting.java b/src/main/java/net/minecraft/world/entity/decoration/Painting.java
|
|
index ad0df80d1adb1d945f40e1b5f7732bb36b2ca2ff..90cab3586d3e3e290475fe8d59a69d89d3c24add 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/Painting.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/Painting.java
|
|
@@ -121,7 +121,7 @@ public class Painting extends HangingEntity implements VariantHolder<Holder<Pain
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(CompoundTag nbt) {
|
|
- Holder<PaintingVariant> holder = loadVariant(nbt).orElseGet(Painting::getDefaultVariant);
|
|
+ Holder<PaintingVariant> holder = loadVariant(nbt).orElseGet(() -> (Holder.Reference<PaintingVariant>) getDefaultVariant()); // Purpur - decompile error TODO: still needed?
|
|
this.setVariant(holder);
|
|
this.direction = Direction.from2DDataValue(nbt.getByte("facing"));
|
|
super.readAdditionalSaveData(nbt);
|
|
@@ -159,7 +159,13 @@ public class Painting extends HangingEntity implements VariantHolder<Holder<Pain
|
|
}
|
|
}
|
|
|
|
- this.spawnAtLocation(Items.PAINTING);
|
|
+ // Purpur start
|
|
+ final ItemStack painting = new ItemStack(Items.PAINTING);
|
|
+ if (this.level.purpurConfig.persistentDroppableEntityDisplayNames && this.hasCustomName()) {
|
|
+ painting.setHoverName(this.getCustomName());
|
|
+ }
|
|
+ this.spawnAtLocation(painting);
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
|
|
index 536856300da929c101f50da5827677bada5feb50..98844ccc6200ba060022525b3a00bcb1de1d0a7a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
|
|
@@ -131,7 +131,7 @@ public class FallingBlockEntity extends Entity {
|
|
@Override
|
|
public void tick() {
|
|
// Paper start - fix sand duping
|
|
- if (this.isRemoved()) {
|
|
+ if (this.level.purpurConfig.fixSandDuping && this.isRemoved()) { // Purpur
|
|
return;
|
|
}
|
|
// Paper end - fix sand duping
|
|
@@ -148,7 +148,7 @@ public class FallingBlockEntity extends Entity {
|
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
|
|
|
// Paper start - fix sand duping
|
|
- if (this.isRemoved()) {
|
|
+ if (this.level.purpurConfig.fixSandDuping && this.isRemoved()) { // Purpur
|
|
return;
|
|
}
|
|
// Paper end - fix sand duping
|
|
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 453f0f7042bdf204db73be139aa36f211c5455e7..52a14af24e3c51c53b40fdb6594b1664eef6d486 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
@@ -55,6 +55,12 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
|
public boolean canMobPickup = true; // Paper
|
|
private int despawnRate = -1; // Paper
|
|
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper
|
|
+ // Purpur start
|
|
+ public boolean immuneToCactus = false;
|
|
+ public boolean immuneToExplosion = false;
|
|
+ public boolean immuneToFire = false;
|
|
+ public boolean immuneToLightning = false;
|
|
+ // Purpur end
|
|
|
|
public ItemEntity(EntityType<? extends ItemEntity> type, Level world) {
|
|
super(type, world);
|
|
@@ -349,6 +355,15 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
|
return false;
|
|
} else if (!this.getItem().getItem().canBeHurtBy(source)) {
|
|
return false;
|
|
+ // Purpur start
|
|
+ } else if (
|
|
+ (immuneToCactus && source.is(net.minecraft.world.damagesource.DamageTypes.CACTUS)) ||
|
|
+ (immuneToFire && (source.is(DamageTypeTags.IS_FIRE) && source.is(net.minecraft.world.damagesource.DamageTypes.IN_FIRE)) ||
|
|
+ (immuneToLightning && source.is(net.minecraft.world.damagesource.DamageTypes.LIGHTNING_BOLT)) ||
|
|
+ (immuneToExplosion && source.is(DamageTypeTags.IS_EXPLOSION)))
|
|
+ ) {
|
|
+ return false;
|
|
+ // Purpur end
|
|
} else if (this.level.isClientSide) {
|
|
return true;
|
|
} else {
|
|
@@ -547,6 +562,12 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
|
this.getEntityData().set(ItemEntity.DATA_ITEM, stack);
|
|
this.getEntityData().markDirty(ItemEntity.DATA_ITEM); // CraftBukkit - SPIGOT-4591, must mark dirty
|
|
this.despawnRate = level.paperConfig().entities.spawning.altItemDespawnRate.enabled ? level.paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), level.spigotConfig.itemDespawnRate) : level.spigotConfig.itemDespawnRate; // Paper
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.itemImmuneToCactus.contains(stack.getItem())) immuneToCactus = true;
|
|
+ if (level.purpurConfig.itemImmuneToExplosion.contains(stack.getItem())) immuneToExplosion = true;
|
|
+ if (level.purpurConfig.itemImmuneToFire.contains(stack.getItem())) immuneToFire = true;
|
|
+ if (level.purpurConfig.itemImmuneToLightning.contains(stack.getItem())) immuneToLightning = true;
|
|
+ // level end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
|
|
index f2094c52196b45adfd51d8aebcc4c46b779b0925..9c8713ef3aeb2ff203bd0328d15d80c2d78d09e9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
|
|
@@ -66,16 +66,19 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
protected AbstractSkeleton(EntityType<? extends AbstractSkeleton> type, Level world) {
|
|
super(type, world);
|
|
this.reassessWeaponGoal();
|
|
+ this.setShouldBurnInDay(true); // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new RestrictSunGoal(this));
|
|
this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
|
@@ -99,35 +102,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
}
|
|
|
|
// Paper start
|
|
- private boolean shouldBurnInDay = true;
|
|
+ // private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity - keep methods for ABI compatibility
|
|
public boolean shouldBurnInDay() { return shouldBurnInDay; }
|
|
public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; }
|
|
// Paper end
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
- boolean flag = shouldBurnInDay && this.isSunBurnTick(); // Paper - Configurable Burning
|
|
-
|
|
- if (flag) {
|
|
- ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
|
-
|
|
- if (!itemstack.isEmpty()) {
|
|
- if (itemstack.isDamageableItem()) {
|
|
- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
|
|
- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
|
|
- this.broadcastBreakEvent(EquipmentSlot.HEAD);
|
|
- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
|
|
- }
|
|
- }
|
|
-
|
|
- flag = false;
|
|
- }
|
|
-
|
|
- if (flag) {
|
|
- this.setSecondsOnFire(8);
|
|
- }
|
|
- }
|
|
-
|
|
+ // Purpur start - implemented in LivingEntity
|
|
super.aiStep();
|
|
}
|
|
|
|
@@ -161,11 +143,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
this.reassessWeaponGoal();
|
|
this.setCanPickUpLoot(this.level.paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.skeletons || randomsource.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); // Paper
|
|
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
|
|
- LocalDate localdate = LocalDate.now();
|
|
- int i = localdate.get(ChronoField.DAY_OF_MONTH);
|
|
- int j = localdate.get(ChronoField.MONTH_OF_YEAR);
|
|
-
|
|
- if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) {
|
|
+ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(world.getMinecraftWorld()) && this.random.nextFloat() < this.level.purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur
|
|
this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
|
|
this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F;
|
|
}
|
|
@@ -192,7 +170,6 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
} else {
|
|
this.goalSelector.addGoal(4, this.meleeGoal);
|
|
}
|
|
-
|
|
}
|
|
}
|
|
|
|
@@ -205,7 +182,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
double d2 = target.getZ() - this.getZ();
|
|
double d3 = Math.sqrt(d0 * d0 + d2 * d2);
|
|
|
|
- entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.level.getDifficulty().getId() * 4));
|
|
+ entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, this.level.purpurConfig.skeletonBowAccuracyMap.getOrDefault(this.level.getDifficulty().getId(), (float) (14 - this.level.getDifficulty().getId() * 4))); // Purpur
|
|
// CraftBukkit start
|
|
org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getMainHandItem(), entityarrow.getPickupItem(), entityarrow, net.minecraft.world.InteractionHand.MAIN_HAND, 0.8F, true); // Paper
|
|
if (event.isCancelled()) {
|
|
@@ -236,7 +213,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
this.reassessWeaponGoal();
|
|
// Paper start
|
|
if (nbt.contains("Paper.ShouldBurnInDay")) {
|
|
- this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
|
|
+ // this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); // Purpur - implemented in LivingEntity
|
|
}
|
|
// Paper end
|
|
}
|
|
@@ -245,7 +222,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
- nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay);
|
|
+ // nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - implemented in LivingEntity
|
|
}
|
|
// Paper end
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Blaze.java b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
|
|
index 5ae34ded698e501dc5cb97b1d7028863e95742a1..2ad81368f731a937303f17ede20f18c978b6479c 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Blaze.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
|
|
@@ -32,26 +32,73 @@ public class Blaze extends Monster {
|
|
|
|
public Blaze(EntityType<? extends Blaze> type, Level world) {
|
|
super(type, world);
|
|
- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur
|
|
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F);
|
|
this.xpReward = 10;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.blazeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.blazeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.blazeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.blazeMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.blazeMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.blazeAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(4, new Blaze.BlazeAttackGoal(this));
|
|
this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F));
|
|
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0D).add(Attributes.MOVEMENT_SPEED, (double)0.23F).add(Attributes.FOLLOW_RANGE, 48.0D);
|
|
+ return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0D).add(Attributes.MOVEMENT_SPEED, (double)0.23F).add(Attributes.FOLLOW_RANGE, 48.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -101,11 +148,19 @@ public class Blaze extends Monster {
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level.purpurConfig.blazeTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
+ // Purpur start
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ setDeltaMovement(mot.x(), getVerticalMot() > 0 ? 0.07D : -0.07D, mot.z());
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
--this.nextHeightOffsetChangeTick;
|
|
if (this.nextHeightOffsetChangeTick <= 0) {
|
|
this.nextHeightOffsetChangeTick = 100;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
|
|
index d980b906d9206560741576fa4153c57212f307a0..d23141c44a11050de6ffd12d95a0c2820c3f71e3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
|
|
@@ -28,6 +28,38 @@ public class CaveSpider extends Spider {
|
|
return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0D);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.caveSpiderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.caveSpiderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.caveSpiderControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.caveSpiderMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.caveSpiderTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.caveSpiderAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean doHurtTarget(Entity target) {
|
|
if (super.doHurtTarget(target)) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
|
|
index 29c62525241e2e03686d1bceee740d4f54f33c54..c32eda28be3eb2c6a6933463d496ea7b6510f27e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
|
|
@@ -59,21 +59,130 @@ public class Creeper extends Monster implements PowerableMob {
|
|
public int maxSwell = 30;
|
|
public int explosionRadius = 3;
|
|
private int droppedSkulls;
|
|
+ // Purpur start
|
|
+ private int spacebarCharge = 0;
|
|
+ private int prevSpacebarCharge = 0;
|
|
+ private int powerToggleDelay = 0;
|
|
+ private boolean exploding = false;
|
|
+ // Purpur end
|
|
|
|
public Creeper(EntityType<? extends Creeper> type, Level world) {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.creeperRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.creeperRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.creeperControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void customServerAiStep() {
|
|
+ if (powerToggleDelay > 0) {
|
|
+ powerToggleDelay--;
|
|
+ }
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ if (getRider().getForwardMot() != 0 || getRider().getStrafeMot() != 0) {
|
|
+ spacebarCharge = 0;
|
|
+ setIgnited(false);
|
|
+ setSwellDir(-1);
|
|
+ }
|
|
+ if (spacebarCharge == prevSpacebarCharge) {
|
|
+ spacebarCharge = 0;
|
|
+ }
|
|
+ prevSpacebarCharge = spacebarCharge;
|
|
+ }
|
|
+ super.customServerAiStep();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ setIgnited(false);
|
|
+ setSwellDir(-1);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (powerToggleDelay > 0) {
|
|
+ return true; // just toggled power, do not jump or ignite
|
|
+ }
|
|
+ spacebarCharge++;
|
|
+ if (spacebarCharge > maxSwell - 2) {
|
|
+ spacebarCharge = 0;
|
|
+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.powered.creeper")) {
|
|
+ powerToggleDelay = 20;
|
|
+ setPowered(!isPowered());
|
|
+ setIgnited(false);
|
|
+ setSwellDir(-1);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ if (!isIgnited()) {
|
|
+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0 &&
|
|
+ getRider().getBukkitEntity().hasPermission("allow.special.creeper")) {
|
|
+ setIgnited(true);
|
|
+ setSwellDir(1);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return getForwardMot() == 0 && getStrafeMot() == 0; // do not jump if standing still
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.creeperMaxHealth);
|
|
+ }
|
|
+
|
|
+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.MobSpawnType spawnReason, @Nullable net.minecraft.world.entity.SpawnGroupData entityData, @Nullable CompoundTag entityNbt) {
|
|
+ double chance = world.getLevel().purpurConfig.creeperChargedChance;
|
|
+ if (chance > 0D && random.nextDouble() <= chance) {
|
|
+ setPowered(true);
|
|
+ }
|
|
+ return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.creeperTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource damagesource) {
|
|
+ if (!exploding && this.level.purpurConfig.creeperExplodeWhenKilled && damagesource.getEntity() instanceof net.minecraft.server.level.ServerPlayer) {
|
|
+ this.explodeCreeper();
|
|
+ }
|
|
+ return super.dropAllDeathLoot(damagesource);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.creeperAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
this.goalSelector.addGoal(2, new SwellGoal(this));
|
|
+ this.goalSelector.addGoal(3, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0D, 1.2D));
|
|
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
|
|
}
|
|
@@ -263,15 +372,17 @@ public class Creeper extends Monster implements PowerableMob {
|
|
}
|
|
|
|
public void explodeCreeper() {
|
|
+ this.exploding = true; // Purpur
|
|
if (!this.level.isClientSide) {
|
|
float f = this.isPowered() ? 2.0F : 1.0F;
|
|
+ float multiplier = this.level.purpurConfig.creeperHealthRadius ? this.getHealth() / this.getMaxHealth() : 1; // Purpur
|
|
|
|
// CraftBukkit start
|
|
- ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), this.explosionRadius * f, false);
|
|
+ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), (this.explosionRadius * f) * multiplier, false); // Purpur
|
|
this.level.getCraftServer().getPluginManager().callEvent(event);
|
|
if (!event.isCancelled()) {
|
|
this.dead = true;
|
|
- this.level.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB);
|
|
+ this.level.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), this.level.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) && level.purpurConfig.creeperAllowGriefing ? Level.ExplosionInteraction.MOB : Level.ExplosionInteraction.NONE); // Purpur
|
|
this.discard();
|
|
this.spawnLingeringCloud();
|
|
} else {
|
|
@@ -280,7 +391,7 @@ public class Creeper extends Monster implements PowerableMob {
|
|
}
|
|
// CraftBukkit end
|
|
}
|
|
-
|
|
+ this.exploding = false; // Purpur
|
|
}
|
|
|
|
private void spawnLingeringCloud() {
|
|
@@ -322,6 +433,7 @@ public class Creeper extends Monster implements PowerableMob {
|
|
com.destroystokyo.paper.event.entity.CreeperIgniteEvent event = new com.destroystokyo.paper.event.entity.CreeperIgniteEvent((org.bukkit.entity.Creeper) getBukkitEntity(), ignited);
|
|
if (event.callEvent()) {
|
|
this.entityData.set(Creeper.DATA_IS_IGNITED, event.isIgnited());
|
|
+ if (!event.isIgnited()) setSwellDir(-1); // Purpur
|
|
}
|
|
}
|
|
// Paper end
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
|
|
index f00773e05654bdeb5463f448293aac99d2208813..a6980d85455234d4f89ff423e013f3c479bd3fe8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
|
|
@@ -29,6 +29,7 @@ import net.minecraft.world.entity.ai.goal.MoveToBlockGoal;
|
|
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
|
|
import net.minecraft.world.entity.ai.goal.RangedAttackGoal;
|
|
import net.minecraft.world.entity.ai.goal.ZombieAttackGoal;
|
|
+import net.minecraft.world.entity.ai.goal.MoveThroughVillageGoal;
|
|
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
|
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
|
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
|
|
@@ -68,6 +69,58 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
this.groundNavigation = new GroundPathNavigation(this, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.drownedRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.drownedRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.drownedControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.drownedMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void randomizeReinforcementsChance() {
|
|
+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.drownedSpawnReinforcements);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level.purpurConfig.drownedJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level.purpurConfig.drownedJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level.purpurConfig.drownedJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.drownedTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.drownedAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void addBehaviourGoals() {
|
|
this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0D));
|
|
@@ -75,10 +128,23 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
this.goalSelector.addGoal(2, new Drowned.DrownedAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(5, new Drowned.DrownedGoToBeachGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(6, new Drowned.DrownedSwimUpGoal(this, 1.0D, this.level.getSeaLevel()));
|
|
+ if (level.purpurConfig.drownedBreakDoors) this.goalSelector.addGoal(6, new MoveThroughVillageGoal(this, 1.0D, true, 4, this::canBreakDoors));
|
|
this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0D));
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Drowned.class})).setAlertOthers(ZombifiedPiglin.class));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::okTarget));
|
|
- if (this.level.spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Paper
|
|
+ // Purpur start
|
|
+ if ( level.spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<AbstractVillager>(this, AbstractVillager.class, false) { // Spigot
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return (level.purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level.getServer().server.isLagging()) && super.canUse();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ return (level.purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level.getServer().server.isLagging()) && super.canContinueToUse();
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Axolotl.class, true, false));
|
|
this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
@@ -112,7 +178,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
|
|
@Override
|
|
public boolean supportsBreakDoorGoal() {
|
|
- return false;
|
|
+ return level.purpurConfig.drownedBreakDoors ? true : false;
|
|
}
|
|
|
|
@Override
|
|
@@ -259,8 +325,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
this.searchingForLand = targetingUnderwater;
|
|
}
|
|
|
|
- private static class DrownedMoveControl extends MoveControl {
|
|
-
|
|
+ private static class DrownedMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
private final Drowned drowned;
|
|
|
|
public DrownedMoveControl(Drowned drowned) {
|
|
@@ -269,7 +334,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
LivingEntity entityliving = this.drowned.getTarget();
|
|
|
|
if (this.drowned.wantsToSwim() && this.drowned.isInWater()) {
|
|
@@ -292,7 +357,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
|
|
this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), f, 90.0F));
|
|
this.drowned.yBodyRot = this.drowned.getYRot();
|
|
- float f1 = (float) (this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float f1 = (float) (this.getSpeedModifier() * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
|
|
float f2 = Mth.lerp(0.125F, this.drowned.getSpeed(), f1);
|
|
|
|
this.drowned.setSpeed(f2);
|
|
@@ -302,7 +367,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
|
this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0D, -0.008D, 0.0D));
|
|
}
|
|
|
|
- super.tick();
|
|
+ super.vanillaTick(); // Purpur
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
|
|
index d02286d553c600fe7e75f48e278e380d21c5b868..916cf5137808003058a787210fc3343d75caf3d9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
|
|
@@ -33,6 +33,33 @@ public class ElderGuardian extends Guardian {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.elderGuardianRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.elderGuardianControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.elderGuardianMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.elderGuardianTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.elderGuardianAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 8.0D).add(Attributes.MAX_HEALTH, 80.0D);
|
|
}
|
|
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 c2f5dabb41b172547864decc06aa632d89dff3e1..7c26e1979cdae52e2e94d24dd8c3164e815226ab 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
@@ -89,12 +89,40 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
public EnderMan(EntityType<? extends EnderMan> type, Level world) {
|
|
super(type, world);
|
|
this.setMaxUpStep(1.0F);
|
|
- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.endermanRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.endermanRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.endermanControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.endermanMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.endermanAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new EnderMan.EndermanFreezeWhenLookedAt(this));
|
|
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F));
|
|
@@ -102,9 +130,10 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
this.goalSelector.addGoal(10, new EnderMan.EndermanLeaveBlockGoal(this));
|
|
this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt));
|
|
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
|
|
- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false));
|
|
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, 10, true, false, (entityliving) -> entityliving.level.purpurConfig.endermanAggroEndermites && entityliving instanceof Endermite endermite && (!entityliving.level.purpurConfig.endermanAggroEndermitesOnlyIfPlayerSpawned || endermite.isPlayerSpawned()))); // Purpur
|
|
this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<>(this, false));
|
|
}
|
|
|
|
@@ -241,7 +270,7 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
// Paper end
|
|
ItemStack itemstack = (ItemStack) player.getInventory().armor.get(3);
|
|
|
|
- if (itemstack.is(Blocks.CARVED_PUMPKIN.asItem())) {
|
|
+ if (this.level.purpurConfig.endermanDisableStareAggro || itemstack.is(Blocks.CARVED_PUMPKIN.asItem()) || (this.level.purpurConfig.endermanIgnorePlayerDragonHead && itemstack.is(net.minecraft.world.item.Items.DRAGON_HEAD))) { // Purpur
|
|
return false;
|
|
} else {
|
|
Vec3 vec3d = player.getViewVector(1.0F).normalize();
|
|
@@ -278,12 +307,12 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level.purpurConfig.endermanTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- if (this.level.isDay() && this.tickCount >= this.targetChangeTime + 600) {
|
|
+ if ((getRider() == null || !this.isControllable()) && this.level.isDay() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - no random teleporting
|
|
float f = this.getLightLevelDependentMagicValue();
|
|
|
|
if (f > 0.5F && this.level.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper
|
|
@@ -404,6 +433,8 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
public boolean hurt(DamageSource source, float amount) {
|
|
if (this.isInvulnerableTo(source)) {
|
|
return false;
|
|
+ } else if (getRider() != null && this.isControllable()) { return super.hurt(source, amount); // Purpur - no teleporting on damage
|
|
+ } else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && source.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL)) { return false; // Purpur - no suffocation damage if short height
|
|
} else {
|
|
boolean flag = source.getDirectEntity() instanceof ThrownPotion;
|
|
boolean flag1;
|
|
@@ -418,6 +449,7 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
} else {
|
|
flag1 = flag && this.hurtWithCleanWater(source, (ThrownPotion) source.getDirectEntity(), amount);
|
|
|
|
+ if (!flag1 && this.level.purpurConfig.endermanIgnoreProjectiles) return super.hurt(source, amount); // Purpur
|
|
if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper start
|
|
for (int i = 0; i < 64; ++i) {
|
|
if (this.teleport()) {
|
|
@@ -464,7 +496,7 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean requiresCustomPersistence() {
|
|
- return super.requiresCustomPersistence() || this.getCarriedBlock() != null;
|
|
+ return super.requiresCustomPersistence() || (!this.level.purpurConfig.endermanDespawnEvenWithBlock && this.getCarriedBlock() != null); // Purpur
|
|
}
|
|
|
|
private static class EndermanFreezeWhenLookedAt extends Goal {
|
|
@@ -511,7 +543,16 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- return this.enderman.getCarriedBlock() == null ? false : (!this.enderman.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0);
|
|
+ if (!enderman.level.purpurConfig.endermanAllowGriefing) return false; // Purpur
|
|
+ // Purpur start
|
|
+ if (this.enderman.getCarriedBlock() == null) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!this.enderman.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !this.enderman.level.purpurConfig.endermanBypassMobGriefing) {
|
|
+ return false;
|
|
+ }
|
|
+ return this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0;
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -558,7 +599,16 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- return this.enderman.getCarriedBlock() != null ? false : (!this.enderman.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0);
|
|
+ if (!enderman.level.purpurConfig.endermanAllowGriefing) return false; // Purpur
|
|
+ // Purpur start
|
|
+ if (this.enderman.getCarriedBlock() != null) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!this.enderman.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !this.enderman.level.purpurConfig.endermanBypassMobGriefing) {
|
|
+ return false;
|
|
+ }
|
|
+ return this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0;
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
|
|
index e8c3972b889fd6b348a5b0d18444d28faa813879..e6ecc47828fea09c80ed3a4c39f0d85f4d820571 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
|
|
@@ -31,20 +31,63 @@ import net.minecraft.world.level.block.state.BlockState;
|
|
public class Endermite extends Monster {
|
|
private static final int MAX_LIFE = 2400;
|
|
public int life;
|
|
+ private boolean isPlayerSpawned; // Purpur
|
|
|
|
public Endermite(EntityType<? extends Endermite> type, Level world) {
|
|
super(type, world);
|
|
this.xpReward = 3;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.endermiteRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.endermiteRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.endermiteControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.endermiteMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.endermiteTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ public boolean isPlayerSpawned() {
|
|
+ return this.isPlayerSpawned;
|
|
+ }
|
|
+
|
|
+ public void setPlayerSpawned(boolean playerSpawned) {
|
|
+ this.isPlayerSpawned = playerSpawned;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.endermiteAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level));
|
|
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
@@ -87,12 +130,14 @@ public class Endermite extends Monster {
|
|
public void readAdditionalSaveData(CompoundTag nbt) {
|
|
super.readAdditionalSaveData(nbt);
|
|
this.life = nbt.getInt("Lifetime");
|
|
+ this.isPlayerSpawned = nbt.getBoolean("PlayerSpawned"); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
nbt.putInt("Lifetime", this.life);
|
|
+ nbt.putBoolean("PlayerSpawned", this.isPlayerSpawned); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
|
|
index 1935f1eb28724d8f03a9612a9b4ddefbbc557157..892e0c0306a21ea638649c1324b8115f24c01bd2 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
|
|
@@ -48,10 +48,43 @@ public class Evoker extends SpellcasterIllager {
|
|
this.xpReward = 10;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.evokerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.evokerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.evokerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.evokerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.evokerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.evokerAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Evoker.EvokerCastingSpellGoal());
|
|
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6D, 1.0D));
|
|
this.goalSelector.addGoal(4, new Evoker.EvokerSummonSpellGoal());
|
|
@@ -60,6 +93,7 @@ public class Evoker extends SpellcasterIllager {
|
|
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
|
|
this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
|
|
@@ -317,7 +351,7 @@ public class Evoker extends SpellcasterIllager {
|
|
return false;
|
|
} else if (Evoker.this.tickCount < this.nextAttackTickCount) {
|
|
return false;
|
|
- } else if (!Evoker.this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ } else if (!Evoker.this.level.purpurConfig.evokerBypassMobGriefing && !Evoker.this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
return false;
|
|
} else {
|
|
List<Sheep> list = Evoker.this.level.getNearbyEntities(Sheep.class, this.wololoTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0D, 4.0D, 16.0D));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
|
|
index bb2cb17e4e5ce142eeec18951c8948e3d6b3209c..77dcae6ecd87fade2b529386ba1360836363593a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
|
|
@@ -44,11 +44,62 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
this.moveControl = new Ghast.GhastMoveControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.ghastRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.ghastRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.ghastControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.ghastMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ghastMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.ghastTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.ghastAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(5, new Ghast.RandomFloatAroundGoal(this));
|
|
this.goalSelector.addGoal(7, new Ghast.GhastLookGoal(this));
|
|
this.goalSelector.addGoal(7, new Ghast.GhastShootFireballGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> {
|
|
return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
|
|
}));
|
|
@@ -103,7 +154,7 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D);
|
|
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -160,7 +211,7 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
return 2.6F;
|
|
}
|
|
|
|
- private static class GhastMoveControl extends MoveControl {
|
|
+ private static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
|
|
|
|
private final Ghast ghast;
|
|
private int floatDuration;
|
|
@@ -171,7 +222,7 @@ public class Ghast extends FlyingMob implements Enemy {
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.operation == MoveControl.Operation.MOVE_TO) {
|
|
if (this.floatDuration-- <= 0) {
|
|
this.floatDuration += this.ghast.getRandom().nextInt(5) + 2;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java
|
|
index 41004c28edb748e12c4f868aa07b4672891197c1..4e5b9f772ba587b4e108add3758dffa665c1c3f3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Giant.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java
|
|
@@ -1,18 +1,123 @@
|
|
package net.minecraft.world.entity.monster;
|
|
|
|
import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.world.Difficulty;
|
|
+import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.entity.EntityDimensions;
|
|
import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.EquipmentSlot;
|
|
+import net.minecraft.world.entity.MobSpawnType;
|
|
import net.minecraft.world.entity.Pose;
|
|
+import net.minecraft.world.entity.SpawnGroupData;
|
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.ai.goal.FloatGoal;
|
|
+import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
|
+import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
|
+import net.minecraft.world.entity.ai.goal.MoveTowardsRestrictionGoal;
|
|
+import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
|
+import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
|
+import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
|
+import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
|
+import net.minecraft.world.entity.animal.IronGolem;
|
|
+import net.minecraft.world.entity.animal.Turtle;
|
|
+import net.minecraft.world.entity.npc.Villager;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelReader;
|
|
+import net.minecraft.world.level.ServerLevelAccessor;
|
|
+
|
|
+import javax.annotation.Nullable;
|
|
|
|
public class Giant extends Monster {
|
|
public Giant(EntityType<? extends Giant> type, Level world) {
|
|
super(type, world);
|
|
+ this.safeFallDistance = 10.0F; // Purpur
|
|
+ }
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.giantRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.giantRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.giantControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.giantMaxHealth);
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level.purpurConfig.giantMovementSpeed);
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level.purpurConfig.giantAttackDamage);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ if (level.purpurConfig.giantHaveAI) {
|
|
+ this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
+ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 16.0F));
|
|
+ this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0D));
|
|
+ if (level.purpurConfig.giantHaveHostileAI) {
|
|
+ this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers(ZombifiedPiglin.class));
|
|
+ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Villager.class, false));
|
|
+ this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
|
+ this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, true));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) {
|
|
+ SpawnGroupData groupData = super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt);
|
|
+ if (groupData == null) {
|
|
+ populateDefaultEquipmentSlots(this.random, difficulty);
|
|
+ populateDefaultEquipmentEnchantments(this.random, difficulty);
|
|
+ }
|
|
+ return groupData;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void populateDefaultEquipmentSlots(net.minecraft.util.RandomSource random, DifficultyInstance difficulty) {
|
|
+ super.populateDefaultEquipmentSlots(this.random, difficulty);
|
|
+ // TODO make configurable
|
|
+ if (random.nextFloat() < (level.getDifficulty() == Difficulty.HARD ? 0.1F : 0.05F)) {
|
|
+ this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SWORD));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ // make giants jump as high as everything else relative to their size
|
|
+ // 1.0 makes bottom of feet about as high as their waist when they jump
|
|
+ return level.purpurConfig.giantJumpHeight;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.giantTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.giantAlwaysDropExp;
|
|
}
|
|
+ // Purpur end
|
|
|
|
@Override
|
|
protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
|
|
@@ -25,6 +130,6 @@ public class Giant extends Monster {
|
|
|
|
@Override
|
|
public float getWalkTargetValue(BlockPos pos, LevelReader world) {
|
|
- return world.getPathfindingCostFromLightLevels(pos);
|
|
+ return super.getWalkTargetValue(pos, world); // Purpur - fix light requirements for natural spawns
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Guardian.java b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
|
|
index cf7e9c1db229f9e2cc05ce3046540db1d4fc4ec4..f10304b38e904528907cb36c342acf9d49935edd 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Guardian.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
|
|
@@ -69,15 +69,51 @@ public class Guardian extends Monster {
|
|
this.xpReward = 10;
|
|
this.setPathfindingMalus(BlockPathTypes.WATER, 0.0F);
|
|
this.moveControl = new Guardian.GuardianMoveControl(this);
|
|
+ // Purpur start
|
|
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
|
|
+ @Override
|
|
+ public void setYawPitch(float yaw, float pitch) {
|
|
+ super.setYawPitch(yaw, pitch * 0.35F);
|
|
+ }
|
|
+ };
|
|
+ // Purpur end
|
|
this.clientSideTailAnimation = this.random.nextFloat();
|
|
this.clientSideTailAnimationO = this.clientSideTailAnimation;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.guardianRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.guardianControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.guardianMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.guardianTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.guardianAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
MoveTowardsRestrictionGoal pathfindergoalmovetowardsrestriction = new MoveTowardsRestrictionGoal(this, 1.0D);
|
|
|
|
this.randomStrollGoal = new RandomStrollGoal(this, 1.0D, 80);
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field
|
|
this.goalSelector.addGoal(5, pathfindergoalmovetowardsrestriction);
|
|
this.goalSelector.addGoal(7, this.randomStrollGoal);
|
|
@@ -86,6 +122,7 @@ public class Guardian extends Monster {
|
|
this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
|
|
this.randomStrollGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
|
pathfindergoalmovetowardsrestriction.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this)));
|
|
}
|
|
|
|
@@ -351,7 +388,7 @@ public class Guardian extends Monster {
|
|
@Override
|
|
public void travel(Vec3 movementInput) {
|
|
if (this.isControlledByLocalInstance() && this.isInWater()) {
|
|
- this.moveRelative(0.1F, movementInput);
|
|
+ this.moveRelative(getRider() != null && this.isControllable() ? getSpeed() : 0.1F, movementInput); // Purpur
|
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
|
this.setDeltaMovement(this.getDeltaMovement().scale(0.9D));
|
|
if (!this.isMoving() && this.getTarget() == null) {
|
|
@@ -363,7 +400,7 @@ public class Guardian extends Monster {
|
|
|
|
}
|
|
|
|
- private static class GuardianMoveControl extends MoveControl {
|
|
+ private static class GuardianMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur
|
|
|
|
private final Guardian guardian;
|
|
|
|
@@ -372,8 +409,17 @@ public class Guardian extends Monster {
|
|
this.guardian = guardian;
|
|
}
|
|
|
|
+ // Purpur start
|
|
@Override
|
|
- public void tick() {
|
|
+ public void purpurTick(Player rider) {
|
|
+ super.purpurTick(rider);
|
|
+ guardian.setDeltaMovement(guardian.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
|
|
+ guardian.setMoving(guardian.getForwardMot() > 0.0F); // control tail speed
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ @Override
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.operation == MoveControl.Operation.MOVE_TO && !this.guardian.getNavigation().isDone()) {
|
|
Vec3 vec3d = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ());
|
|
double d0 = vec3d.length();
|
|
@@ -384,7 +430,7 @@ public class Guardian extends Monster {
|
|
|
|
this.guardian.setYRot(this.rotlerp(this.guardian.getYRot(), f, 90.0F));
|
|
this.guardian.yBodyRot = this.guardian.getYRot();
|
|
- float f1 = (float) (this.speedModifier * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float f1 = (float) (this.getSpeedModifier() * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
|
|
float f2 = Mth.lerp(0.125F, this.guardian.getSpeed(), f1);
|
|
|
|
this.guardian.setSpeed(f2);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java
|
|
index 4996347c6dde85a2dc9aa37fdf495160093fac64..a7b690c0730d0b10133f24d7ce2d9f6a0e4a7c04 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Husk.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java
|
|
@@ -20,15 +20,68 @@ public class Husk extends Zombie {
|
|
|
|
public Husk(EntityType<? extends Husk> type, Level world) {
|
|
super(type, world);
|
|
+ this.setShouldBurnInDay(false); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.huskRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.huskRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.huskControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.huskMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void randomizeReinforcementsChance() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.huskSpawnReinforcements);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level.purpurConfig.huskJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level.purpurConfig.huskJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level.purpurConfig.huskJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.huskTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.huskAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static boolean checkHuskSpawnRules(EntityType<Husk> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
|
return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (spawnReason == MobSpawnType.SPAWNER || world.canSeeSky(pos));
|
|
}
|
|
|
|
@Override
|
|
public boolean isSunSensitive() {
|
|
- return false;
|
|
+ return this.shouldBurnInDay; // Purpur - moved to LivingEntity - keep methods for ABI compatibility
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
|
|
index 10573602c9bc73713cbd6989762d3dbb6f6fcf8c..63577d941dbd21cf93bc6f88bb50922618b6b5d5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
|
|
@@ -59,10 +59,45 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.illusionerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.illusionerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.illusionerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void initAttributes() {
|
|
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level.purpurConfig.illusionerMovementSpeed);
|
|
+ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level.purpurConfig.illusionerFollowRange);
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.illusionerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.illusionerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.illusionerAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new SpellcasterIllager.SpellcasterCastingSpellGoal());
|
|
this.goalSelector.addGoal(4, new Illusioner.IllusionerMirrorSpellGoal());
|
|
this.goalSelector.addGoal(5, new Illusioner.IllusionerBlindnessSpellGoal());
|
|
@@ -70,6 +105,7 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
|
|
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
|
|
this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
|
|
index f23d8796aec3e02a3bb23f338903f39b6ef9dcf1..11af95207f5aff52427dc216fb9929b0f536f411 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
|
|
@@ -25,6 +25,58 @@ public class MagmaCube extends Slime {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.magmaCubeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.magmaCubeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.magmaCubeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ return 0.42F * this.getBlockJumpFactor(); // from EntityLiving
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected String getMaxHealthEquation() {
|
|
+ return level.purpurConfig.magmaCubeMaxHealth;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected String getAttackDamageEquation() {
|
|
+ return level.purpurConfig.magmaCubeAttackDamage;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected java.util.Map<Integer, Double> getMaxHealthCache() {
|
|
+ return level.purpurConfig.magmaCubeMaxHealthCache;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected java.util.Map<Integer, Double> getAttackDamageCache() {
|
|
+ return level.purpurConfig.magmaCubeAttackDamageCache;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.magmaCubeTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.magmaCubeAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, (double)0.2F);
|
|
}
|
|
@@ -70,10 +122,11 @@ public class MagmaCube extends Slime {
|
|
}
|
|
|
|
@Override
|
|
- protected void jumpFromGround() {
|
|
+ public void jumpFromGround() { // Purpur - protected -> public
|
|
Vec3 vec3 = this.getDeltaMovement();
|
|
this.setDeltaMovement(vec3.x, (double)(this.getJumpPower() + (float)this.getSize() * 0.1F), vec3.z);
|
|
this.hasImpulse = true;
|
|
+ this.actualJump = false; // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Monster.java b/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
|
index 55c245d0dfa369dc6de2197ae37335fba4fae4ae..c9b40515f4c2ff1eedfc9510930c3baebc078ebd 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Monster.java
|
|
@@ -89,6 +89,14 @@ public abstract class Monster extends PathfinderMob implements Enemy {
|
|
}
|
|
|
|
public static boolean isDarkEnoughToSpawn(ServerLevelAccessor world, BlockPos pos, RandomSource random) {
|
|
+ // Purpur start
|
|
+ if (!world.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce || !world.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce) {
|
|
+ net.minecraft.world.level.block.state.BlockState spawnBlock = world.getBlockState(pos.below());
|
|
+ if ((!world.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.PACKED_ICE)) || (!world.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.BLUE_ICE))) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
if (world.getBrightness(LightLayer.SKY, pos) > random.nextInt(32)) {
|
|
return false;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
|
|
index 97fb1d2110a51498f6419841081b500b3f190370..2c00a9fdd3a6ea16ee765339857cf58521c85797 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
|
|
@@ -49,6 +49,8 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
Vec3 moveTargetPoint;
|
|
public BlockPos anchorPoint;
|
|
Phantom.AttackPhase attackPhase;
|
|
+ Vec3 crystalPosition; // Purpur
|
|
+ private static final net.minecraft.world.item.crafting.Ingredient TORCH = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.item.Items.TORCH, net.minecraft.world.item.Items.SOUL_TORCH); // Purpur
|
|
|
|
public Phantom(EntityType<? extends Phantom> type, Level world) {
|
|
super(type, world);
|
|
@@ -58,8 +60,110 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
this.xpReward = 5;
|
|
this.moveControl = new Phantom.PhantomMoveControl(this);
|
|
this.lookControl = new Phantom.PhantomLookControl(this);
|
|
+ this.setShouldBurnInDay(true); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.phantomRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.phantomRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.phantomControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.phantomMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable() && !onGround) {
|
|
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() {
|
|
+ return Monster.createMonsterAttributes().add(Attributes.FLYING_SPEED, 3.0D);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.special.phantom")) {
|
|
+ shoot();
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public boolean shoot() {
|
|
+ org.bukkit.Location loc = ((org.bukkit.entity.LivingEntity) getBukkitEntity()).getEyeLocation();
|
|
+ loc.setPitch(-loc.getPitch());
|
|
+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(100).add(loc.toVector());
|
|
+
|
|
+ org.purpurmc.purpur.entity.PhantomFlames flames = new org.purpurmc.purpur.entity.PhantomFlames(level, this);
|
|
+ flames.canGrief = level.purpurConfig.phantomAllowGriefing;
|
|
+ flames.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), 1.0F, 5.0F);
|
|
+ level.addFreshEntity(flames);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ private double getFromCache(java.util.function.Supplier<String> equation, java.util.function.Supplier<java.util.Map<Integer, Double>> cache, java.util.function.Supplier<Double> defaultValue) {
|
|
+ int size = getPhantomSize();
|
|
+ Double value = cache.get().get(size);
|
|
+ if (value == null) {
|
|
+ try {
|
|
+ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ value = defaultValue.get();
|
|
+ }
|
|
+ cache.get().put(size, value);
|
|
+ }
|
|
+ return value;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected net.minecraft.world.level.storage.loot.LootContext.Builder createLootContext(boolean causedByPlayer, DamageSource source) {
|
|
+ boolean dropped = false;
|
|
+ if (lastHurtByPlayer == null && source.getEntity() instanceof net.minecraft.world.entity.boss.enderdragon.EndCrystal) {
|
|
+ if (random.nextInt(5) < 1) {
|
|
+ dropped = spawnAtLocation(new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.PHANTOM_MEMBRANE)) != null;
|
|
+ }
|
|
+ }
|
|
+ if (!dropped) {
|
|
+ return super.createLootContext(causedByPlayer, source);
|
|
+ }
|
|
+ return new net.minecraft.world.level.storage.loot.LootContext.Builder((net.minecraft.server.level.ServerLevel) level);
|
|
+ }
|
|
+
|
|
+ public boolean isCirclingCrystal() {
|
|
+ return crystalPosition != null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.phantomTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.phantomAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean isFlapping() {
|
|
return (this.getUniqueFlapTickOffset() + this.tickCount) % Phantom.TICKS_PER_FLAP == 0;
|
|
@@ -72,9 +176,17 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
- this.goalSelector.addGoal(1, new Phantom.PhantomAttackStrategyGoal());
|
|
- this.goalSelector.addGoal(2, new Phantom.PhantomSweepAttackGoal());
|
|
- this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal());
|
|
+ // Purpur start
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ if (level.purpurConfig.phantomOrbitCrystalRadius > 0) {
|
|
+ this.goalSelector.addGoal(1, new FindCrystalGoal(this));
|
|
+ this.goalSelector.addGoal(2, new OrbitCrystalGoal(this));
|
|
+ }
|
|
+ this.goalSelector.addGoal(3, new Phantom.PhantomAttackStrategyGoal());
|
|
+ this.goalSelector.addGoal(4, new Phantom.PhantomSweepAttackGoal());
|
|
+ this.goalSelector.addGoal(5, new Phantom.PhantomCircleAroundAnchorGoal());
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ // Purpur end
|
|
this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal());
|
|
}
|
|
|
|
@@ -90,7 +202,10 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
|
|
private void updatePhantomSizeInfo() {
|
|
this.refreshDimensions();
|
|
- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) (6 + this.getPhantomSize()));
|
|
+ // Purpur start
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(() -> this.level.purpurConfig.phantomMaxHealth, () -> this.level.purpurConfig.phantomMaxHealthCache, () -> 20.0D));
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(() -> this.level.purpurConfig.phantomAttackDamage, () -> this.level.purpurConfig.phantomAttackDamageCache, () -> (double) 6 + this.getPhantomSize()));
|
|
+ // Purpur end
|
|
}
|
|
|
|
public int getPhantomSize() {
|
|
@@ -140,14 +255,12 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
this.level.addParticle(ParticleTypes.MYCELIUM, this.getX() - (double) f2, this.getY() + (double) f4, this.getZ() - (double) f3, 0.0D, 0.0D, 0.0D);
|
|
}
|
|
|
|
+ if (level.purpurConfig.phantomFlamesOnSwoop && attackPhase == AttackPhase.SWOOP) shoot(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
- if (this.isAlive() && shouldBurnInDay && this.isSunBurnTick()) { // Paper - Configurable Burning
|
|
- this.setSecondsOnFire(8);
|
|
- }
|
|
-
|
|
+ // Purpur - moved down to shouldBurnInDay()
|
|
super.aiStep();
|
|
}
|
|
|
|
@@ -159,7 +272,11 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) {
|
|
this.anchorPoint = this.blockPosition().above(5);
|
|
- this.setPhantomSize(0);
|
|
+ // Purpur start
|
|
+ int min = world.getLevel().purpurConfig.phantomMinSize;
|
|
+ int max = world.getLevel().purpurConfig.phantomMaxSize;
|
|
+ this.setPhantomSize(min == max ? min : world.getRandom().nextInt(max + 1 - min) + min);
|
|
+ // Purpur end
|
|
return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt);
|
|
}
|
|
|
|
@@ -175,7 +292,7 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
if (nbt.hasUUID("Paper.SpawningEntity")) {
|
|
this.spawningEntity = nbt.getUUID("Paper.SpawningEntity");
|
|
}
|
|
- if (nbt.contains("Paper.ShouldBurnInDay")) {
|
|
+ if (false && nbt.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity
|
|
this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
|
|
}
|
|
// Paper end
|
|
@@ -192,7 +309,7 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
if (this.spawningEntity != null) {
|
|
nbt.putUUID("Paper.SpawningEntity", this.spawningEntity);
|
|
}
|
|
- nbt.putBoolean("Paper.ShouldBurnInDay", shouldBurnInDay);
|
|
+ // nbt.putBoolean("Paper.ShouldBurnInDay", shouldBurnInDay); // Purpur - implemented in LivingEntity
|
|
// Paper end
|
|
}
|
|
|
|
@@ -258,8 +375,14 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
}
|
|
public void setSpawningEntity(java.util.UUID entity) { this.spawningEntity = entity; }
|
|
|
|
- private boolean shouldBurnInDay = true;
|
|
- public boolean shouldBurnInDay() { return shouldBurnInDay; }
|
|
+ // private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity - keep methods for ABI compatibility
|
|
+ // Purpur start
|
|
+ public boolean shouldBurnInDay() {
|
|
+ boolean burnFromDaylight = this.shouldBurnInDay && this.level.purpurConfig.phantomBurnInDaylight;
|
|
+ boolean burnFromLightSource = this.level.purpurConfig.phantomBurnInLight > 0 && this.level.getMaxLocalRawBrightness(blockPosition()) >= this.level.purpurConfig.phantomBurnInLight;
|
|
+ return burnFromDaylight || burnFromLightSource;
|
|
+ }
|
|
+ // Purpur End
|
|
public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; }
|
|
// Paper end
|
|
private static enum AttackPhase {
|
|
@@ -269,7 +392,125 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
private AttackPhase() {}
|
|
}
|
|
|
|
- private class PhantomMoveControl extends MoveControl {
|
|
+ // Purpur start
|
|
+ class FindCrystalGoal extends Goal {
|
|
+ private final Phantom phantom;
|
|
+ private net.minecraft.world.entity.boss.enderdragon.EndCrystal crystal;
|
|
+ private Comparator<net.minecraft.world.entity.boss.enderdragon.EndCrystal> comparator;
|
|
+
|
|
+ FindCrystalGoal(Phantom phantom) {
|
|
+ this.phantom = phantom;
|
|
+ this.comparator = Comparator.comparingDouble(phantom::distanceToSqr);
|
|
+ this.setFlags(EnumSet.of(Flag.LOOK));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ double range = maxTargetRange();
|
|
+ List<net.minecraft.world.entity.boss.enderdragon.EndCrystal> crystals = level.getEntitiesOfClass(net.minecraft.world.entity.boss.enderdragon.EndCrystal.class, phantom.getBoundingBox().inflate(range));
|
|
+ if (crystals.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ crystals.sort(comparator);
|
|
+ crystal = crystals.get(0);
|
|
+ if (phantom.distanceToSqr(crystal) > range * range) {
|
|
+ crystal = null;
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ if (crystal == null || !crystal.isAlive()) {
|
|
+ return false;
|
|
+ }
|
|
+ double range = maxTargetRange();
|
|
+ return phantom.distanceToSqr(crystal) <= (range * range) * 2;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ phantom.crystalPosition = new Vec3(crystal.getX(), crystal.getY() + (phantom.random.nextInt(10) + 10), crystal.getZ());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void stop() {
|
|
+ crystal = null;
|
|
+ phantom.crystalPosition = null;
|
|
+ super.stop();
|
|
+ }
|
|
+
|
|
+ private double maxTargetRange() {
|
|
+ return phantom.level.purpurConfig.phantomOrbitCrystalRadius;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ class OrbitCrystalGoal extends Goal {
|
|
+ private final Phantom phantom;
|
|
+ private float offset;
|
|
+ private float radius;
|
|
+ private float verticalChange;
|
|
+ private float direction;
|
|
+
|
|
+ OrbitCrystalGoal(Phantom phantom) {
|
|
+ this.phantom = phantom;
|
|
+ this.setFlags(EnumSet.of(Flag.MOVE));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return phantom.isCirclingCrystal();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ this.radius = 5.0F + phantom.random.nextFloat() * 10.0F;
|
|
+ this.verticalChange = -4.0F + phantom.random.nextFloat() * 9.0F;
|
|
+ this.direction = phantom.random.nextBoolean() ? 1.0F : -1.0F;
|
|
+ updateOffset();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (phantom.random.nextInt(350) == 0) {
|
|
+ this.verticalChange = -4.0F + phantom.random.nextFloat() * 9.0F;
|
|
+ }
|
|
+ if (phantom.random.nextInt(250) == 0) {
|
|
+ ++this.radius;
|
|
+ if (this.radius > 15.0F) {
|
|
+ this.radius = 5.0F;
|
|
+ this.direction = -this.direction;
|
|
+ }
|
|
+ }
|
|
+ if (phantom.random.nextInt(450) == 0) {
|
|
+ this.offset = phantom.random.nextFloat() * 2.0F * 3.1415927F;
|
|
+ updateOffset();
|
|
+ }
|
|
+ if (phantom.moveTargetPoint.distanceToSqr(phantom.getX(), phantom.getY(), phantom.getZ()) < 4.0D) {
|
|
+ updateOffset();
|
|
+ }
|
|
+ if (phantom.moveTargetPoint.y < phantom.getY() && !phantom.level.isEmptyBlock(new BlockPos(phantom).below(1))) {
|
|
+ this.verticalChange = Math.max(1.0F, this.verticalChange);
|
|
+ updateOffset();
|
|
+ }
|
|
+ if (phantom.moveTargetPoint.y > phantom.getY() && !phantom.level.isEmptyBlock(new BlockPos(phantom).above(1))) {
|
|
+ this.verticalChange = Math.min(-1.0F, this.verticalChange);
|
|
+ updateOffset();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void updateOffset() {
|
|
+ this.offset += this.direction * 15.0F * 0.017453292F;
|
|
+ phantom.moveTargetPoint = phantom.crystalPosition.add(
|
|
+ this.radius * Mth.cos(this.offset),
|
|
+ -4.0F + this.verticalChange,
|
|
+ this.radius * Mth.sin(this.offset));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
+ private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
|
|
|
|
private float speed = 0.1F;
|
|
|
|
@@ -277,8 +518,19 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
super(entity);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void purpurTick(Player rider) {
|
|
+ if (!Phantom.this.onGround) {
|
|
+ // phantom is always in motion when flying
|
|
+ // TODO - FIX THIS
|
|
+ // rider.setForward(1.0F);
|
|
+ }
|
|
+ super.purpurTick(rider);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (Phantom.this.horizontalCollision) {
|
|
Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F);
|
|
this.speed = 0.1F;
|
|
@@ -324,14 +576,20 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
}
|
|
}
|
|
|
|
- private class PhantomLookControl extends LookControl {
|
|
+ private class PhantomLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
public PhantomLookControl(Mob entity) {
|
|
super(entity);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void purpurTick(Player rider) {
|
|
+ setYawPitch(rider.getYRot(), -rider.xRotO * 0.75F);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
- public void tick() {}
|
|
+ public void vanillaTick() {} // Purpur
|
|
}
|
|
|
|
private class PhantomBodyRotationControl extends BodyRotationControl {
|
|
@@ -418,6 +676,12 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
return false;
|
|
} else if (!entityliving.isAlive()) {
|
|
return false;
|
|
+ // Purpur start
|
|
+ } else if (level.purpurConfig.phantomBurnInLight > 0 && level.getLightEmission(new BlockPos(Phantom.this)) >= level.purpurConfig.phantomBurnInLight) {
|
|
+ return false;
|
|
+ } else if (level.purpurConfig.phantomIgnorePlayersWithTorch && (TORCH.test(entityliving.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(entityliving.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)))) {
|
|
+ return false;
|
|
+ // Purpur end
|
|
} else {
|
|
if (entityliving instanceof Player) {
|
|
Player entityhuman = (Player) entityliving;
|
|
@@ -563,6 +827,7 @@ public class Phantom extends FlyingMob implements Enemy {
|
|
this.nextScanTick = reducedTickDelay(60);
|
|
List<Player> list = Phantom.this.level.getNearbyPlayers(this.attackTargeting, Phantom.this, Phantom.this.getBoundingBox().inflate(16.0D, 64.0D, 16.0D));
|
|
|
|
+ if (level.purpurConfig.phantomIgnorePlayersWithTorch) list.removeIf(human -> TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)));// Purpur
|
|
if (!list.isEmpty()) {
|
|
list.sort(Comparator.comparing((Entity e) -> { return e.getY(); }).reversed()); // CraftBukkit - decompile error
|
|
Iterator iterator = list.iterator();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
|
|
index cec545c3baa6599d47b9cf1a4b97de8771062a22..06a96eb0ef40462932892c611f308eb31411d099 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
|
|
@@ -62,15 +62,49 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.pillagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.pillagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.pillagerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pillagerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.pillagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.pillagerAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new Raider.HoldGroundAttackGoal(this, 10.0F));
|
|
this.goalSelector.addGoal(3, new RangedCrossbowAttackGoal<>(this, 1.0D, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 15.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 15.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, Raider.class)).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
|
|
index 9258d0f7c5c27b6d3d8f99db947169d6800d8ea9..0099595a5daa9c0ca9e3fd35933038c1c8ecf009 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
|
|
@@ -65,14 +65,54 @@ public class Ravager extends Raider {
|
|
this.setPathfindingMalus(BlockPathTypes.LEAVES, 0.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.ravagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.ravagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.ravagerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onMount(Player rider) {
|
|
+ super.onMount(rider);
|
|
+ getNavigation().stop();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ravagerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.ravagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.ravagerAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(4, new Ravager.RavagerMeleeAttackGoal());
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(2, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, (entityliving) -> {
|
|
@@ -150,7 +190,7 @@ public class Ravager extends Raider {
|
|
@Override
|
|
public void aiStep() {
|
|
super.aiStep();
|
|
- if (this.isAlive()) {
|
|
+ if (this.isAlive() && (getRider() == null || !this.isControllable())) { // Purpur
|
|
if (this.isImmobile()) {
|
|
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0D);
|
|
} else {
|
|
@@ -160,7 +200,7 @@ public class Ravager extends Raider {
|
|
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(Mth.lerp(0.1D, d1, d0));
|
|
}
|
|
|
|
- if (this.horizontalCollision && this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (this.horizontalCollision && (this.level.purpurConfig.ravagerBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur
|
|
boolean flag = false;
|
|
AABB axisalignedbb = this.getBoundingBox().inflate(0.2D);
|
|
Iterator iterator = BlockPos.betweenClosed(Mth.floor(axisalignedbb.minX), Mth.floor(axisalignedbb.minY), Mth.floor(axisalignedbb.minZ), Mth.floor(axisalignedbb.maxX), Mth.floor(axisalignedbb.maxY), Mth.floor(axisalignedbb.maxZ)).iterator();
|
|
@@ -170,7 +210,7 @@ public class Ravager extends Raider {
|
|
BlockState iblockdata = this.level.getBlockState(blockposition);
|
|
Block block = iblockdata.getBlock();
|
|
|
|
- if (block instanceof LeavesBlock && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.getFluidState().createLegacyBlock()).isCancelled()) { // CraftBukkit // Paper
|
|
+ if (this.level.purpurConfig.ravagerGriefableBlocks.contains(block) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.getFluidState().createLegacyBlock()).isCancelled()) { // CraftBukkit // Paper
|
|
flag = this.level.destroyBlock(blockposition, true, this) || flag;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
|
|
index 8cb910da17d75a9d9c7dbeb3c9e24b6de657a2f7..8b03a027bff592b2257e065f328da6d86e11db98 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
|
|
@@ -22,6 +22,8 @@ import net.minecraft.tags.DamageTypeTags;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.Difficulty;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntityDimensions;
|
|
@@ -50,6 +52,8 @@ import net.minecraft.world.entity.projectile.AbstractArrow;
|
|
import net.minecraft.world.entity.projectile.ShulkerBullet;
|
|
import net.minecraft.world.entity.vehicle.Boat;
|
|
import net.minecraft.world.item.DyeColor;
|
|
+import net.minecraft.world.item.DyeItem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.ServerLevelAccessor;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
@@ -98,12 +102,59 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
this.lookControl = new Shulker.ShulkerLookControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.shulkerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.shulkerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.shulkerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.shulkerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.shulkerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ ItemStack itemstack = player.getItemInHand(hand);
|
|
+ if (player.level.purpurConfig.shulkerChangeColorWithDye && itemstack.getItem() instanceof DyeItem dye && dye.getDyeColor() != this.getColor()) {
|
|
+ this.setVariant(Optional.of(dye.getDyeColor()));
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ return super.mobInteract(player, hand);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.shulkerAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F, 0.02F, true));
|
|
this.goalSelector.addGoal(4, new Shulker.ShulkerAttackGoal());
|
|
this.goalSelector.addGoal(7, new Shulker.ShulkerPeekGoal());
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{this.getClass()})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new Shulker.ShulkerNearestAttackGoal(this));
|
|
this.targetSelector.addGoal(3, new Shulker.ShulkerDefenseAttackGoal(this));
|
|
@@ -484,11 +535,20 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
Vec3 vec3d = this.position();
|
|
AABB axisalignedbb = this.getBoundingBox();
|
|
|
|
- if (!this.isClosed() && this.teleportSomewhere()) {
|
|
- int i = this.level.getEntities((EntityTypeTest) EntityType.SHULKER, axisalignedbb.inflate(8.0D), Entity::isAlive).size();
|
|
- float f = (float) (i - 1) / 5.0F;
|
|
-
|
|
- if (this.level.random.nextFloat() >= f) {
|
|
+ if ((!this.level.purpurConfig.shulkerSpawnFromBulletRequireOpenLid || !this.isClosed()) && this.teleportSomewhere()) {
|
|
+ // Purpur start
|
|
+ float chance = this.level.purpurConfig.shulkerSpawnFromBulletBaseChance;
|
|
+ if (!this.level.purpurConfig.shulkerSpawnFromBulletNearbyEquation.isBlank()) {
|
|
+ int nearby = this.level.getEntities((EntityTypeTest) EntityType.SHULKER, axisalignedbb.inflate(this.level.purpurConfig.shulkerSpawnFromBulletNearbyRange), Entity::isAlive).size();
|
|
+ try {
|
|
+ chance -= ((Number) scriptEngine.eval("let nearby = " + nearby + "; " + this.level.purpurConfig.shulkerSpawnFromBulletNearbyEquation)).floatValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ chance -= (nearby - 1) / 5.0F;
|
|
+ }
|
|
+ }
|
|
+ if (this.level.random.nextFloat() <= chance) {
|
|
+ // Purpur end
|
|
Shulker entityshulker = (Shulker) EntityType.SHULKER.create(this.level);
|
|
|
|
if (entityshulker != null) {
|
|
@@ -601,7 +661,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
|
|
@Override
|
|
public Optional<DyeColor> getVariant() {
|
|
- return Optional.ofNullable(this.getColor());
|
|
+ return Optional.ofNullable(this.level.purpurConfig.shulkerSpawnFromBulletRandomColor ? DyeColor.random(this.level.random) : this.getColor()); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
@@ -611,7 +671,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
|
|
return b0 != 16 && b0 <= 15 ? DyeColor.byId(b0) : null;
|
|
}
|
|
|
|
- private class ShulkerLookControl extends LookControl {
|
|
+ private class ShulkerLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
|
|
|
|
public ShulkerLookControl(Mob entity) {
|
|
super(entity);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
|
|
index cd1098acc2bec9db2f242738a04f9fae3a66c1bf..202fe776c9275571138aabd230bec2fa0a985bb1 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
|
|
@@ -42,14 +42,48 @@ public class Silverfish extends Monster {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.silverfishRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.silverfishRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.silverfishControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.silverfishMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.silverfishTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.silverfishAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level));
|
|
this.goalSelector.addGoal(3, this.friendsGoal);
|
|
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
|
|
this.goalSelector.addGoal(5, new Silverfish.SilverfishMergeWithStoneGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
@@ -184,7 +218,7 @@ public class Silverfish extends Monster {
|
|
continue;
|
|
}
|
|
// CraftBukkit end
|
|
- if (world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
|
|
+ if (world.purpurConfig.silverfishBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur
|
|
world.destroyBlock(blockposition1, true, this.silverfish);
|
|
} else {
|
|
world.setBlock(blockposition1, ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)), 3);
|
|
@@ -222,7 +256,7 @@ public class Silverfish extends Monster {
|
|
} else {
|
|
RandomSource randomsource = this.mob.getRandom();
|
|
|
|
- if (this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) {
|
|
+ if ((this.mob.level.purpurConfig.silverfishBypassMobGriefing || this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && randomsource.nextInt(reducedTickDelay(10)) == 0) { // Purpur
|
|
this.selectedDirection = Direction.getRandom(randomsource);
|
|
BlockPos blockposition = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5D, this.mob.getZ()).relative(this.selectedDirection);
|
|
BlockState iblockdata = this.mob.level.getBlockState(blockposition);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
|
|
index badde621357a567965f0ef203e402e21bed09059..64a5e000adbfa5de2abc32ea9182847dbf83293d 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
|
|
@@ -14,6 +14,16 @@ import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.level.Level;
|
|
|
|
+// Purpur start
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.InteractionResult;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.core.particles.ParticleTypes;
|
|
+// Purpur end
|
|
+
|
|
public class Skeleton extends AbstractSkeleton {
|
|
|
|
private static final int TOTAL_CONVERSION_TIME = 300;
|
|
@@ -26,6 +36,38 @@ public class Skeleton extends AbstractSkeleton {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.skeletonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.skeletonRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.skeletonControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.skeletonMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.skeletonTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.skeletonAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
@@ -142,4 +184,67 @@ public class Skeleton extends AbstractSkeleton {
|
|
}
|
|
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ private int witherRosesFed = 0;
|
|
+
|
|
+ @Override
|
|
+ public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
+ ItemStack stack = player.getItemInHand(hand);
|
|
+
|
|
+ if (level.purpurConfig.skeletonFeedWitherRoses > 0 && this.getType() != EntityType.WITHER_SKELETON && stack.getItem() == Blocks.WITHER_ROSE.asItem()) {
|
|
+ return this.feedWitherRose(player, stack);
|
|
+ }
|
|
+
|
|
+ return super.mobInteract(player, hand);
|
|
+ }
|
|
+
|
|
+ private InteractionResult feedWitherRose(Player player, ItemStack stack) {
|
|
+ if (++witherRosesFed < level.purpurConfig.skeletonFeedWitherRoses) {
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+ return InteractionResult.CONSUME;
|
|
+ }
|
|
+
|
|
+ WitherSkeleton skeleton = EntityType.WITHER_SKELETON.create(level);
|
|
+ if (skeleton == null) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+
|
|
+ skeleton.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
|
|
+ skeleton.setHealth(this.getHealth());
|
|
+ skeleton.setAggressive(this.isAggressive());
|
|
+ skeleton.copyPosition(this);
|
|
+ skeleton.setYBodyRot(this.yBodyRot);
|
|
+ skeleton.setYHeadRot(this.getYHeadRot());
|
|
+ skeleton.yRotO = this.yRotO;
|
|
+ skeleton.xRotO = this.xRotO;
|
|
+
|
|
+ if (this.hasCustomName()) {
|
|
+ skeleton.setCustomName(this.getCustomName());
|
|
+ }
|
|
+
|
|
+ if (CraftEventFactory.callEntityTransformEvent(this, skeleton, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+
|
|
+ if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), skeleton.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.INFECTED).callEvent()) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+
|
|
+ this.level.addFreshEntity(skeleton);
|
|
+ this.remove(RemovalReason.DISCARDED);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ stack.shrink(1);
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < 15; ++i) {
|
|
+ ((ServerLevel) level).sendParticles(((ServerLevel) level).players(), null, ParticleTypes.HAPPY_VILLAGER,
|
|
+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1,
|
|
+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true);
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
|
index c41c1c2712920c6b7d822cd0f37a5d8d725e4054..89978fcb14362af2527693f3e6ec57e169080c9f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java
|
|
@@ -64,6 +64,7 @@ public class Slime extends Mob implements Enemy {
|
|
public float squish;
|
|
public float oSquish;
|
|
private boolean wasOnGround;
|
|
+ protected boolean actualJump; // Purpur
|
|
|
|
public Slime(EntityType<? extends Slime> type, Level world) {
|
|
super(type, world);
|
|
@@ -71,12 +72,89 @@ public class Slime extends Mob implements Enemy {
|
|
this.moveControl = new Slime.SlimeMoveControl(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.slimeRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.slimeRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.slimeControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getJumpPower() {
|
|
+ float height = super.getJumpPower();
|
|
+ return getRider() != null && this.isControllable() && actualJump ? height * 1.5F : height;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean onSpacebar() {
|
|
+ if (onGround && getRider() != null && this.isControllable()) {
|
|
+ actualJump = true;
|
|
+ if (getRider().getForwardMot() == 0 || getRider().getStrafeMot() == 0) {
|
|
+ jumpFromGround(); // jump() here if not moving
|
|
+ }
|
|
+ }
|
|
+ return true; // do not jump() in wasd controller, let vanilla controller handle
|
|
+ }
|
|
+
|
|
+ protected String getMaxHealthEquation() {
|
|
+ return level.purpurConfig.slimeMaxHealth;
|
|
+ }
|
|
+
|
|
+ protected String getAttackDamageEquation() {
|
|
+ return level.purpurConfig.slimeAttackDamage;
|
|
+ }
|
|
+
|
|
+ protected java.util.Map<Integer, Double> getMaxHealthCache() {
|
|
+ return level.purpurConfig.slimeMaxHealthCache;
|
|
+ }
|
|
+
|
|
+ protected java.util.Map<Integer, Double> getAttackDamageCache() {
|
|
+ return level.purpurConfig.slimeAttackDamageCache;
|
|
+ }
|
|
+
|
|
+ protected double getFromCache(java.util.function.Supplier<String> equation, java.util.function.Supplier<java.util.Map<Integer, Double>> cache, java.util.function.Supplier<Double> defaultValue) {
|
|
+ int size = getSize();
|
|
+ Double value = cache.get().get(size);
|
|
+ if (value == null) {
|
|
+ try {
|
|
+ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ value = defaultValue.get();
|
|
+ }
|
|
+ cache.get().put(size, value);
|
|
+ }
|
|
+ return value;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.slimeTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.slimeAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Slime.SlimeFloatGoal(this));
|
|
this.goalSelector.addGoal(2, new Slime.SlimeAttackGoal(this));
|
|
this.goalSelector.addGoal(3, new Slime.SlimeRandomDirectionGoal(this));
|
|
this.goalSelector.addGoal(5, new Slime.SlimeKeepOnJumpingGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> {
|
|
return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
|
|
}));
|
|
@@ -96,9 +174,9 @@ public class Slime extends Mob implements Enemy {
|
|
this.entityData.set(Slime.ID_SIZE, j);
|
|
this.reapplyPosition();
|
|
this.refreshDimensions();
|
|
- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double) (j * j));
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(this::getMaxHealthEquation, this::getMaxHealthCache, () -> (double) size * size)); // Purpur
|
|
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue((double) (0.2F + 0.1F * (float) j));
|
|
- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) j);
|
|
+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(this::getAttackDamageEquation, this::getAttackDamageCache, () -> (double) j)); // Purpur
|
|
if (heal) {
|
|
this.setHealth(this.getMaxHealth());
|
|
}
|
|
@@ -368,11 +446,12 @@ public class Slime extends Mob implements Enemy {
|
|
}
|
|
|
|
@Override
|
|
- protected void jumpFromGround() {
|
|
+ public void jumpFromGround() { // Purpur - protected -> public
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
|
|
this.setDeltaMovement(vec3d.x, (double) this.getJumpPower(), vec3d.z);
|
|
this.hasImpulse = true;
|
|
+ this.actualJump = false; // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
@@ -406,7 +485,7 @@ public class Slime extends Mob implements Enemy {
|
|
return super.getDimensions(pose).scale(0.255F * (float) this.getSize());
|
|
}
|
|
|
|
- private static class SlimeMoveControl extends MoveControl {
|
|
+ private static class SlimeMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
|
|
|
|
private float yRot;
|
|
private int jumpDelay;
|
|
@@ -425,21 +504,33 @@ public class Slime extends Mob implements Enemy {
|
|
}
|
|
|
|
public void setWantedMovement(double speed) {
|
|
- this.speedModifier = speed;
|
|
+ this.setSpeedModifier(speed); // Purpur
|
|
this.operation = MoveControl.Operation.MOVE_TO;
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (slime.getRider() != null && slime.isControllable()) {
|
|
+ purpurTick(slime.getRider());
|
|
+ if (slime.getForwardMot() != 0 || slime.getStrafeMot() != 0) {
|
|
+ if (jumpDelay > 10) {
|
|
+ jumpDelay = 6;
|
|
+ }
|
|
+ } else {
|
|
+ jumpDelay = 20;
|
|
+ }
|
|
+ } else {
|
|
+ // Purpur end
|
|
this.mob.setYRot(this.rotlerp(this.mob.getYRot(), this.yRot, 90.0F));
|
|
this.mob.yHeadRot = this.mob.getYRot();
|
|
this.mob.yBodyRot = this.mob.getYRot();
|
|
- if (this.operation != MoveControl.Operation.MOVE_TO) {
|
|
+ } if ((slime.getRider() == null || !slime.isControllable()) && this.operation != MoveControl.Operation.MOVE_TO) { // Purpur
|
|
this.mob.setZza(0.0F);
|
|
} else {
|
|
this.operation = MoveControl.Operation.WAIT;
|
|
if (this.mob.isOnGround()) {
|
|
- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
|
|
+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur
|
|
if (this.jumpDelay-- <= 0) {
|
|
this.jumpDelay = this.slime.getJumpDelay();
|
|
if (this.isAggressive) {
|
|
@@ -456,7 +547,7 @@ public class Slime extends Mob implements Enemy {
|
|
this.mob.setSpeed(0.0F);
|
|
}
|
|
} else {
|
|
- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
|
|
+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
|
index 0c36bb47bd7040f1544817810e1c87157cdaff96..8e071a0922164970e033029c12058db9e8da261a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
|
@@ -51,14 +51,48 @@ public class Spider extends Monster {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.spiderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.spiderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.spiderControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.spiderMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.spiderTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.spiderAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4F));
|
|
this.goalSelector.addGoal(4, new Spider.SpiderAttackGoal(this));
|
|
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
|
|
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
|
|
this.targetSelector.addGoal(2, new Spider.SpiderTargetGoal<>(this, Player.class));
|
|
this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal<>(this, IronGolem.class));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Stray.java b/src/main/java/net/minecraft/world/entity/monster/Stray.java
|
|
index 118b636a44e4b062e812e433f603b039276337da..677b304c177a1e2bdaddf3044b44a06395ee6b6e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Stray.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java
|
|
@@ -21,6 +21,38 @@ public class Stray extends AbstractSkeleton {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.strayRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.strayRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.strayControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.strayMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.strayTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.strayAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static boolean checkStraySpawnRules(EntityType<Stray> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
|
BlockPos blockPos = pos;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java
|
|
index 66755efc54059dfb8625f028bf0548d188a57aa2..0b5d3837536d526c25ba1e12be142bb476d03519 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Strider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java
|
|
@@ -94,12 +94,44 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
super(type, world);
|
|
this.steering = new ItemBasedSteering(this.entityData, Strider.DATA_BOOST_TIME, Strider.DATA_SADDLE_ID);
|
|
this.blocksBuilding = true;
|
|
- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
|
|
+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur
|
|
this.setPathfindingMalus(BlockPathTypes.LAVA, 0.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.striderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.striderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.striderControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.striderMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.striderBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.striderAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static boolean checkStriderSpawnRules(EntityType<Strider> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
|
BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable();
|
|
|
|
@@ -161,6 +193,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.panicGoal = new PanicGoal(this, 1.65D);
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, this.panicGoal);
|
|
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
|
|
this.temptGoal = new TemptGoal(this, 1.4D, Strider.TEMPT_ITEMS, false);
|
|
@@ -416,7 +449,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
|
|
@Override
|
|
public boolean isSensitiveToWater() {
|
|
- return true;
|
|
+ return this.level.purpurConfig.striderTakeDamageFromWater; // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -458,6 +491,19 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
boolean flag = this.isFood(player.getItemInHand(hand));
|
|
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.striderGiveSaddleBack && player.isSecondaryUseActive() && !flag && isSaddled() && !isVehicle()) {
|
|
+ this.steering.setSaddle(false);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ ItemStack saddle = new ItemStack(Items.SADDLE);
|
|
+ if (!player.getInventory().add(saddle)) {
|
|
+ player.drop(saddle, false);
|
|
+ }
|
|
+ }
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (!flag && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) {
|
|
if (!this.level.isClientSide) {
|
|
player.startRiding(this);
|
|
@@ -470,7 +516,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
|
|
if (!enuminteractionresult.consumesAction()) {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
- return itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : InteractionResult.PASS;
|
|
+ return itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : tryRide(player, hand); // Purpur
|
|
} else {
|
|
if (flag && !this.isSilent()) {
|
|
this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.STRIDER_EAT, this.getSoundSource(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java
|
|
index bb5c2f90bef5e3c57ffde996853e122d108b2789..4c4c4d52e2be963024106783b4d28713f125e2e6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Vex.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java
|
|
@@ -63,6 +63,65 @@ public class Vex extends Monster implements TraceableEntity {
|
|
this.xpReward = 3;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.vexRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.vexRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.vexControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double getMaxY() {
|
|
+ return level.purpurConfig.vexMaxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void travel(Vec3 vec3) {
|
|
+ super.travel(vec3);
|
|
+ if (getRider() != null && this.isControllable()) {
|
|
+ float speed;
|
|
+ if (onGround) {
|
|
+ speed = (float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F;
|
|
+ } else {
|
|
+ speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
|
|
+ }
|
|
+ setSpeed(speed);
|
|
+ Vec3 mot = getDeltaMovement();
|
|
+ move(MoverType.SELF, mot.multiply(speed, 1.0, speed));
|
|
+ setDeltaMovement(mot.scale(0.9D));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) {
|
|
+ return false; // no fall damage please
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.vexMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.vexTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.vexAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
|
|
return dimensions.height - 0.28125F;
|
|
@@ -81,7 +140,7 @@ public class Vex extends Monster implements TraceableEntity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
- this.noPhysics = true;
|
|
+ this.noPhysics = getRider() == null || !this.isControllable(); // Purpur
|
|
super.tick();
|
|
this.noPhysics = false;
|
|
this.setNoGravity(true);
|
|
@@ -96,17 +155,19 @@ public class Vex extends Monster implements TraceableEntity {
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(4, new Vex.VexChargeAttackGoal());
|
|
this.goalSelector.addGoal(8, new Vex.VexRandomMoveGoal());
|
|
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new Vex.VexCopyOwnerTargetGoal(this));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D);
|
|
+ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur;
|
|
}
|
|
|
|
@Override
|
|
@@ -235,14 +296,14 @@ public class Vex extends Monster implements TraceableEntity {
|
|
return 0.4D;
|
|
}
|
|
|
|
- private class VexMoveControl extends MoveControl {
|
|
+ private class VexMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
|
|
|
|
public VexMoveControl(Vex entityvex) {
|
|
super(entityvex);
|
|
}
|
|
|
|
@Override
|
|
- public void tick() {
|
|
+ public void vanillaTick() { // Purpur
|
|
if (this.operation == MoveControl.Operation.MOVE_TO) {
|
|
Vec3 vec3d = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ());
|
|
double d0 = vec3d.length();
|
|
@@ -251,7 +312,7 @@ public class Vex extends Monster implements TraceableEntity {
|
|
this.operation = MoveControl.Operation.WAIT;
|
|
Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5D));
|
|
} else {
|
|
- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.speedModifier * 0.05D / d0)));
|
|
+ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.getSpeedModifier() * 0.05D / d0))); // Purpur
|
|
if (Vex.this.getTarget() == null) {
|
|
Vec3 vec3d1 = Vex.this.getDeltaMovement();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
|
|
index a9e75a16a7dc0ff5d4f0faa92ebc444559a39325..1a333dce35a13b88cb0afdea192585e0bae38442 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
|
|
@@ -58,14 +58,48 @@ public class Vindicator extends AbstractIllager {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.vindicatorRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.vindicatorRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.vindicatorControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.vindicatorMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.vindicatorTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.vindicatorAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(1, new Vindicator.VindicatorBreakDoorGoal(this));
|
|
this.goalSelector.addGoal(2, new AbstractIllager.RaiderOpenDoorGoal(this));
|
|
this.goalSelector.addGoal(3, new Raider.HoldGroundAttackGoal(this, 10.0F));
|
|
this.goalSelector.addGoal(4, new Vindicator.VindicatorMeleeAttackGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, Raider.class)).setAlertOthers());
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true));
|
|
@@ -130,6 +164,12 @@ public class Vindicator extends AbstractIllager {
|
|
RandomSource randomSource = world.getRandom();
|
|
this.populateDefaultEquipmentSlots(randomSource, difficulty);
|
|
this.populateDefaultEquipmentEnchantments(randomSource, difficulty);
|
|
+ // Purpur start
|
|
+ Level level = world.getMinecraftWorld();
|
|
+ if (level.purpurConfig.vindicatorJohnnySpawnChance > 0D && random.nextDouble() <= level.purpurConfig.vindicatorJohnnySpawnChance) {
|
|
+ setCustomName(Component.translatable("Johnny"));
|
|
+ }
|
|
+ // Purpur end
|
|
return spawnGroupData;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java
|
|
index 096546d7a97f031060bda7545aa620d522766719..dcf8cdb8343706b55df206fed70fe3a8373e27a6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Witch.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java
|
|
@@ -57,6 +57,38 @@ public class Witch extends Raider implements RangedAttackMob {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.witchRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.witchRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.witchControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.witchMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.witchTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.witchAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
@@ -65,10 +97,12 @@ public class Witch extends Raider implements RangedAttackMob {
|
|
});
|
|
this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, (Predicate) null);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 60, 10.0F));
|
|
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(3, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[]{Raider.class}));
|
|
this.targetSelector.addGoal(2, this.healRaidersGoal);
|
|
this.targetSelector.addGoal(3, this.attackPlayersGoal);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
|
|
index 6449213d717271bcc516e393a78dfe1e5c762d68..9016fb6da9c86ca9906f6beb2f6927cede50c804 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
|
|
@@ -35,6 +35,38 @@ public class WitherSkeleton extends AbstractSkeleton {
|
|
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.witherSkeletonRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.witherSkeletonRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.witherSkeletonControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.witherSkeletonMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.witherSkeletonTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.witherSkeletonAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true));
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
|
|
index 5956a7759964f5e4939f062e93714fba64f53141..052eac2c63ecaa052c9fe6ea3d3d1000da8a33fa 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
|
|
@@ -67,6 +67,38 @@ public class Zoglin extends Monster implements Enemy, HoglinBase {
|
|
this.xpReward = 5;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.zoglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.zoglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.zoglinControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.zoglinMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.zoglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.zoglinAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected Brain.Provider<Zoglin> brainProvider() {
|
|
return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
|
|
@@ -182,7 +214,7 @@ public class Zoglin extends Monster implements Enemy, HoglinBase {
|
|
|
|
@Override
|
|
public Brain<Zoglin> getBrain() {
|
|
- return super.getBrain();
|
|
+ return (Brain<Zoglin>) super.getBrain(); // Purpur - decompile error
|
|
}
|
|
|
|
protected void updateActivity() {
|
|
@@ -198,9 +230,10 @@ public class Zoglin extends Monster implements Enemy, HoglinBase {
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("zoglinBrain");
|
|
+ //this.level.getProfiler().push("zoglinBrain"); // Purpur
|
|
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
|
|
this.getBrain().tick((ServerLevel)this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
this.updateActivity();
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
|
index 9976205537cfe228735687f1e9c52c74ac025690..36d37e544e342e1bc584374580dbb5c883523204 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
|
@@ -95,22 +95,69 @@ public class Zombie extends Monster {
|
|
private int inWaterTime;
|
|
public int conversionTime;
|
|
private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
|
|
- private boolean shouldBurnInDay = true; // Paper
|
|
+ // private boolean shouldBurnInDay = true; // Paper // Purpur - implemented in LivingEntity
|
|
|
|
public Zombie(EntityType<? extends Zombie> type, Level world) {
|
|
super(type, world);
|
|
this.breakDoorGoal = new BreakDoorGoal(this, com.google.common.base.Predicates.in(world.paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(type, world.paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.ZOMBIE)))); // Paper
|
|
+ this.setShouldBurnInDay(true); // Purpur
|
|
}
|
|
|
|
public Zombie(Level world) {
|
|
this(EntityType.ZOMBIE, world);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.zombieRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.zombieRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.zombieControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.zombieMaxHealth);
|
|
+ }
|
|
+
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level.purpurConfig.zombieJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ public double jockeyChance() {
|
|
+ return level.purpurConfig.zombieJockeyChance;
|
|
+ }
|
|
+
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level.purpurConfig.zombieJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.zombieTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.zombieAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
if (level.paperConfig().entities.behavior.zombiesTargetTurtleEggs) this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0D, 3)); // Paper
|
|
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
|
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
this.addBehaviourGoals();
|
|
}
|
|
|
|
@@ -120,7 +167,19 @@ public class Zombie extends Monster {
|
|
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D));
|
|
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers(ZombifiedPiglin.class));
|
|
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
|
- if ( level.spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Spigot
|
|
+ // Purpur start
|
|
+ if ( level.spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<AbstractVillager>(this, AbstractVillager.class, false) { // Spigot
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return (level.purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level.getServer().server.isLagging()) && super.canUse();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ return (level.purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level.getServer().server.isLagging()) && super.canContinueToUse();
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
|
|
this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
@@ -242,30 +301,7 @@ public class Zombie extends Monster {
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
- if (this.isAlive()) {
|
|
- boolean flag = this.isSunSensitive() && this.isSunBurnTick();
|
|
-
|
|
- if (flag) {
|
|
- ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
|
-
|
|
- if (!itemstack.isEmpty()) {
|
|
- if (itemstack.isDamageableItem()) {
|
|
- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
|
|
- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
|
|
- this.broadcastBreakEvent(EquipmentSlot.HEAD);
|
|
- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
|
|
- }
|
|
- }
|
|
-
|
|
- flag = false;
|
|
- }
|
|
-
|
|
- if (flag) {
|
|
- this.setSecondsOnFire(8);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
+ // Purpur - implemented in LivingEntity
|
|
super.aiStep();
|
|
}
|
|
|
|
@@ -303,6 +339,7 @@ public class Zombie extends Monster {
|
|
|
|
}
|
|
|
|
+ public boolean shouldBurnInDay() { return isSunSensitive(); } // Purpur - for ABI compatibility
|
|
public boolean isSunSensitive() {
|
|
return this.shouldBurnInDay; // Paper - use api value instead
|
|
}
|
|
@@ -432,7 +469,7 @@ public class Zombie extends Monster {
|
|
nbt.putBoolean("CanBreakDoors", this.canBreakDoors());
|
|
nbt.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1);
|
|
nbt.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1);
|
|
- nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper
|
|
+ // nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper // Purpur - implemented in LivingEntity
|
|
}
|
|
|
|
@Override
|
|
@@ -446,7 +483,7 @@ public class Zombie extends Monster {
|
|
}
|
|
// Paper start
|
|
if (nbt.contains("Paper.ShouldBurnInDay")) {
|
|
- this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
|
|
+ // this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); // Purpur - implemented in LivingEntity
|
|
}
|
|
// Paper end
|
|
|
|
@@ -527,19 +564,20 @@ public class Zombie extends Monster {
|
|
if (object instanceof Zombie.ZombieGroupData) {
|
|
Zombie.ZombieGroupData entityzombie_groupdatazombie = (Zombie.ZombieGroupData) object;
|
|
|
|
- if (entityzombie_groupdatazombie.isBaby) {
|
|
- this.setBaby(true);
|
|
+ // Purpur start
|
|
+ if (!jockeyOnlyBaby() || entityzombie_groupdatazombie.isBaby) {
|
|
+ this.setBaby(entityzombie_groupdatazombie.isBaby);
|
|
if (entityzombie_groupdatazombie.canSpawnJockey) {
|
|
- if ((double) randomsource.nextFloat() < 0.05D) {
|
|
- List<Chicken> list = world.getEntitiesOfClass(Chicken.class, this.getBoundingBox().inflate(5.0D, 3.0D, 5.0D), EntitySelector.ENTITY_NOT_BEING_RIDDEN);
|
|
+ if ((double) randomsource.nextFloat() < jockeyChance()) {
|
|
+ List<Chicken> list = jockeyTryExistingChickens() ? world.getEntitiesOfClass(Chicken.class, this.getBoundingBox().inflate(5.0D, 3.0D, 5.0D), EntitySelector.ENTITY_NOT_BEING_RIDDEN) : java.util.Collections.emptyList();
|
|
+ // Purpur end
|
|
|
|
if (!list.isEmpty()) {
|
|
Chicken entitychicken = (Chicken) list.get(0);
|
|
|
|
entitychicken.setChickenJockey(true);
|
|
this.startRiding(entitychicken);
|
|
- }
|
|
- } else if ((double) randomsource.nextFloat() < 0.05D) {
|
|
+ } else { // Purpur
|
|
Chicken entitychicken1 = (Chicken) EntityType.CHICKEN.create(this.level);
|
|
|
|
if (entitychicken1 != null) {
|
|
@@ -549,6 +587,7 @@ public class Zombie extends Monster {
|
|
this.startRiding(entitychicken1);
|
|
world.addFreshEntity(entitychicken1, CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit
|
|
}
|
|
+ } // Purpur
|
|
}
|
|
}
|
|
}
|
|
@@ -559,11 +598,7 @@ public class Zombie extends Monster {
|
|
}
|
|
|
|
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
|
|
- LocalDate localdate = LocalDate.now();
|
|
- int i = localdate.get(ChronoField.DAY_OF_MONTH);
|
|
- int j = localdate.get(ChronoField.MONTH_OF_YEAR);
|
|
-
|
|
- if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) {
|
|
+ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(world.getMinecraftWorld()) && this.random.nextFloat() < this.level.purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur
|
|
this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
|
|
this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F;
|
|
}
|
|
@@ -595,7 +630,7 @@ public class Zombie extends Monster {
|
|
}
|
|
|
|
protected void randomizeReinforcementsChance() {
|
|
- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.10000000149011612D);
|
|
+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.zombieSpawnReinforcements); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
|
|
index 71a36cf9b976443cca9ab63cd0eb23253f638562..0c6e8e05014125427513e96c32510125ec34ece9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
|
|
@@ -79,6 +79,58 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
|
|
});
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.zombieVillagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.zombieVillagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.zombieVillagerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.zombieVillagerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void randomizeReinforcementsChance() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.zombieVillagerSpawnReinforcements);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level.purpurConfig.zombieVillagerJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level.purpurConfig.zombieVillagerJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level.purpurConfig.zombieVillagerJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.zombieVillagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.zombieVillagerAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
@@ -165,13 +217,13 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (itemstack.is(Items.GOLDEN_APPLE)) {
|
|
- if (this.hasEffect(MobEffects.WEAKNESS)) {
|
|
+ if (this.hasEffect(MobEffects.WEAKNESS) && level.purpurConfig.zombieVillagerCureEnabled) { // Purpur
|
|
if (!player.getAbilities().instabuild) {
|
|
itemstack.shrink(1);
|
|
}
|
|
|
|
if (!this.level.isClientSide) {
|
|
- this.startConverting(player.getUUID(), this.random.nextInt(2401) + 3600);
|
|
+ this.startConverting(player.getUUID(), this.random.nextInt(level.purpurConfig.zombieVillagerCuringTimeMax - level.purpurConfig.zombieVillagerCuringTimeMin + 1) + level.purpurConfig.zombieVillagerCuringTimeMin); // Purpur
|
|
}
|
|
|
|
return InteractionResult.SUCCESS;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
|
|
index b75945807b425609394c343da56c316a769f0a29..d3bcfa017967db0a20c18c65e27c2a0471d2214e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
|
|
@@ -63,6 +63,53 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.zombifiedPiglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.zombifiedPiglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.zombifiedPiglinControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.zombifiedPiglinMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyOnlyBaby() {
|
|
+ return level.purpurConfig.zombifiedPiglinJockeyOnlyBaby;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double jockeyChance() {
|
|
+ return level.purpurConfig.zombifiedPiglinJockeyChance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean jockeyTryExistingChickens() {
|
|
+ return level.purpurConfig.zombifiedPiglinJockeyTryExistingChickens;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.zombifiedPiglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.zombifiedPiglinAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void setPersistentAngerTarget(@Nullable UUID angryAt) {
|
|
this.persistentAngerTarget = angryAt;
|
|
@@ -115,7 +162,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
this.maybeAlertOthers();
|
|
}
|
|
|
|
- if (this.isAngry()) {
|
|
+ if (this.isAngry() && this.level.purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur
|
|
this.lastHurtByPlayerTime = this.tickCount;
|
|
}
|
|
|
|
@@ -170,7 +217,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
this.ticksUntilNextAlert = ZombifiedPiglin.ALERT_INTERVAL.sample(this.random);
|
|
}
|
|
|
|
- if (entityliving instanceof Player) {
|
|
+ if (entityliving instanceof Player && this.level.purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur
|
|
this.setLastHurtByPlayer((Player) entityliving);
|
|
}
|
|
|
|
@@ -250,7 +297,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
|
|
|
|
@Override
|
|
protected void randomizeReinforcementsChance() {
|
|
- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0D);
|
|
+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.zombifiedPiglinSpawnReinforcements); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
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 daa2224b021c966751eb39f269ffbfe6e7f3d426..63ffb8c04f1408d028af086ba907551a05b96e72 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
|
|
@@ -67,6 +67,43 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
|
|
this.xpReward = 5;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.hoglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.hoglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.hoglinControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.hoglinMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getPurpurBreedTime() {
|
|
+ return this.level.purpurConfig.hoglinBreedingTicks;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.hoglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.hoglinAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public boolean canBeLeashed(Player player) {
|
|
return !this.isLeashed();
|
|
@@ -129,10 +166,10 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
|
|
private int behaviorTick; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("hoglinBrain");
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ //this.level.getProfiler().push("hoglinBrain"); // Purpur
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick((ServerLevel)this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
HoglinAi.updateActivity(this);
|
|
if (this.isConverting()) {
|
|
++this.timeInOverworld;
|
|
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 b401fb4f276ca81b4bb18426ee56abed8a9f7a7b..c8db72dbdaedddb712f20cf0e3a9c731312307a0 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
|
|
@@ -97,6 +97,38 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
|
|
this.xpReward = 5;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.piglinRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.piglinRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.piglinControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.piglinMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.piglinTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.piglinAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag nbt) {
|
|
super.addAdditionalSaveData(nbt);
|
|
@@ -311,10 +343,10 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
|
|
private int behaviorTick; // Pufferfish
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("piglinBrain");
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ //this.level.getProfiler().push("piglinBrain"); // Purpur
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick((ServerLevel) this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
PiglinAi.updateActivity(this);
|
|
super.customServerAiStep();
|
|
}
|
|
@@ -410,7 +442,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
|
|
|
|
@Override
|
|
public boolean wantsToPickUp(ItemStack stack) {
|
|
- return this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack);
|
|
+ return (this.level.purpurConfig.piglinBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack);
|
|
}
|
|
|
|
protected boolean canReplaceCurrentItem(ItemStack stack) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
|
|
index ac75c54e897565e340b66823caeed92ba1d1641a..df4d1745c4957e564bab11d68a37178fdb398543 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
|
|
@@ -41,6 +41,38 @@ public class PiglinBrute extends AbstractPiglin {
|
|
this.xpReward = 20;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.piglinBruteRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.piglinBruteRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.piglinBruteControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.piglinBruteMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.piglinBruteTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.piglinBruteAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 50.0D).add(Attributes.MOVEMENT_SPEED, (double)0.35F).add(Attributes.ATTACK_DAMAGE, 7.0D);
|
|
}
|
|
@@ -70,7 +102,7 @@ public class PiglinBrute extends AbstractPiglin {
|
|
|
|
@Override
|
|
public Brain<PiglinBrute> getBrain() {
|
|
- return super.getBrain();
|
|
+ return (Brain<PiglinBrute>) super.getBrain(); // Purpur - decompile error
|
|
}
|
|
|
|
@Override
|
|
@@ -85,9 +117,10 @@ public class PiglinBrute extends AbstractPiglin {
|
|
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
- this.level.getProfiler().push("piglinBruteBrain");
|
|
+ //this.level.getProfiler().push("piglinBruteBrain"); // Purpur
|
|
+ if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider
|
|
this.getBrain().tick((ServerLevel)this.level, this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
PiglinBruteAi.updateActivity(this);
|
|
PiglinBruteAi.maybePlayActivitySound(this);
|
|
super.customServerAiStep();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
|
|
index 907d77dd74066c723238155b42028a811365b1f8..69e5b4b6c8d5725bc2fb7cd819219e4ff9df45bd 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
|
|
@@ -120,8 +120,32 @@ public class Warden extends Monster implements VibrationListener.VibrationListen
|
|
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F);
|
|
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F);
|
|
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.5F); // Purpur
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.wardenRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.wardenRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.wardenControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public Packet<ClientGamePacketListener> getAddEntityPacket() {
|
|
return new ClientboundAddEntityPacket(this, this.hasPose(Pose.EMERGING) ? 1 : 0);
|
|
@@ -275,10 +299,10 @@ public class Warden extends Monster implements VibrationListener.VibrationListen
|
|
protected void customServerAiStep() {
|
|
ServerLevel worldserver = (ServerLevel) this.level;
|
|
|
|
- worldserver.getProfiler().push("wardenBrain");
|
|
+ //worldserver.getProfiler().push("wardenBrain"); // Purpur
|
|
if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
this.getBrain().tick(worldserver, this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
super.customServerAiStep();
|
|
if ((this.tickCount + this.getId()) % 120 == 0) {
|
|
Warden.applyDarknessAround(worldserver, this.position(), this, 20);
|
|
@@ -405,19 +429,16 @@ public class Warden extends Monster implements VibrationListener.VibrationListen
|
|
|
|
@Contract("null->false")
|
|
public boolean canTargetEntity(@Nullable Entity entity) {
|
|
- boolean flag;
|
|
-
|
|
+ if (getRider() != null && isControllable()) return false; // Purpur
|
|
if (entity instanceof LivingEntity) {
|
|
LivingEntity entityliving = (LivingEntity) entity;
|
|
|
|
if (this.level == entity.level && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity) && !this.isAlliedTo(entity) && entityliving.getType() != EntityType.ARMOR_STAND && entityliving.getType() != EntityType.WARDEN && !entityliving.isInvulnerable() && !entityliving.isDeadOrDying() && this.level.getWorldBorder().isWithinBounds(entityliving.getBoundingBox())) {
|
|
- flag = true;
|
|
- return flag;
|
|
+ return true; // Purpur - wtf
|
|
}
|
|
}
|
|
|
|
- flag = false;
|
|
- return flag;
|
|
+ return false; // Purpur - wtf
|
|
}
|
|
|
|
public static void applyDarknessAround(ServerLevel world, Vec3 pos, @Nullable Entity entity, int range) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
|
|
index ca96b893e22de3ae7c11d5cded51edf70bdcb6f2..d4ab27de59e9c533789f062e74ceb453483e2e39 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
|
|
@@ -43,6 +43,7 @@ import org.bukkit.event.entity.VillagerAcquireTradeEvent;
|
|
// CraftBukkit end
|
|
|
|
public abstract class AbstractVillager extends AgeableMob implements InventoryCarrier, Npc, Merchant {
|
|
+ static final net.minecraft.world.item.crafting.Ingredient TEMPT_ITEMS = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.level.block.Blocks.EMERALD_BLOCK.asItem()); // Purpur
|
|
|
|
// CraftBukkit start
|
|
private CraftMerchant craftMerchant;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java b/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java
|
|
index 5f407535298a31a34cfe114dd863fd6a9b977707..29c7e33fe961020e5a0007287fe9b6631689f1b8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java
|
|
@@ -30,7 +30,7 @@ public class CatSpawner implements CustomSpawner {
|
|
if (this.nextTick > 0) {
|
|
return 0;
|
|
} else {
|
|
- this.nextTick = 1200;
|
|
+ this.nextTick = world.purpurConfig.catSpawnDelay; // Purpur
|
|
Player player = world.getRandomPlayer();
|
|
if (player == null) {
|
|
return 0;
|
|
@@ -63,11 +63,15 @@ public class CatSpawner implements CustomSpawner {
|
|
}
|
|
|
|
private int spawnInVillage(ServerLevel world, BlockPos pos) {
|
|
- int i = 48;
|
|
+ // Purpur start
|
|
+ int range = world.purpurConfig.catSpawnVillageScanRange;
|
|
+ if (range <= 0) return 0;
|
|
+
|
|
if (world.getPoiManager().getCountInRange((entry) -> {
|
|
return entry.is(PoiTypes.HOME);
|
|
- }, pos, 48, PoiManager.Occupancy.IS_OCCUPIED) > 4L) {
|
|
- List<Cat> list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(48.0D, 8.0D, 48.0D));
|
|
+ }, pos, range, PoiManager.Occupancy.IS_OCCUPIED) > 4L) {
|
|
+ List<Cat> list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(range, 8.0D, range));
|
|
+ // Purpur end
|
|
if (list.size() < 5) {
|
|
return this.spawnCat(pos, world);
|
|
}
|
|
@@ -77,8 +81,11 @@ public class CatSpawner implements CustomSpawner {
|
|
}
|
|
|
|
private int spawnInHut(ServerLevel world, BlockPos pos) {
|
|
- int i = 16;
|
|
- List<Cat> list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(16.0D, 8.0D, 16.0D));
|
|
+ // Purpur start
|
|
+ int range = world.purpurConfig.catSpawnSwampHutScanRange;
|
|
+ if (range <= 0) return 0;
|
|
+ List<Cat> list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(range, 8.0D, range));
|
|
+ // Purpur end
|
|
return list.size() < 1 ? this.spawnCat(pos, world) : 0;
|
|
}
|
|
|
|
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 5402a084ef5fe0b3cfea897a90cffade1eff5b66..73cdb6b1793b264e1ec8ff51c4f8274366a7d4d7 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
@@ -139,6 +139,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> {
|
|
return holder.is(PoiTypes.MEETING);
|
|
});
|
|
+ private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur
|
|
+ private int notLobotomizedCount = 0; // Purpur
|
|
|
|
public long nextGolemPanic = -1; // Pufferfish
|
|
|
|
@@ -155,6 +157,90 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE));
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.villagerRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.villagerRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.villagerControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void registerGoals() {
|
|
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
|
|
+ if (level.purpurConfig.villagerFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.villagerMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canBeLeashed(Player player) {
|
|
+ return level.purpurConfig.villagerCanBeLeashed && !this.isLeashed();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.villagerTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.villagerAlwaysDropExp;
|
|
+ }
|
|
+
|
|
+ private boolean checkLobotomized() {
|
|
+ int interval = this.level.purpurConfig.villagerLobotomizeCheckInterval;
|
|
+ if (this.notLobotomizedCount > 3) {
|
|
+ // check half as often if not lobotomized for the last 3+ consecutive checks
|
|
+ interval *= 2;
|
|
+ }
|
|
+ if (this.level.getGameTime() % interval == 0) {
|
|
+ // offset Y for short blocks like dirt_path/farmland
|
|
+ this.isLobotomized = !canTravelFrom(BlockPos.containing(this.position().x, this.getBoundingBox().minY + 0.0625D, this.position().z));
|
|
+
|
|
+ if (this.isLobotomized) {
|
|
+ this.notLobotomizedCount = 0;
|
|
+ } else {
|
|
+ this.notLobotomizedCount++;
|
|
+ }
|
|
+ }
|
|
+ return this.isLobotomized;
|
|
+ }
|
|
+
|
|
+ private boolean canTravelFrom(BlockPos pos) {
|
|
+ return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south());
|
|
+ }
|
|
+
|
|
+ private boolean canTravelTo(BlockPos pos) {
|
|
+ net.minecraft.world.level.block.state.BlockState state = this.level.getBlockStateIfLoaded(pos);
|
|
+ if (state == null) {
|
|
+ // chunk not loaded
|
|
+ return false;
|
|
+ }
|
|
+ net.minecraft.world.level.block.Block bottom = state.getBlock();
|
|
+ if (bottom instanceof net.minecraft.world.level.block.FenceBlock ||
|
|
+ bottom instanceof net.minecraft.world.level.block.FenceGateBlock ||
|
|
+ bottom instanceof net.minecraft.world.level.block.WallBlock) {
|
|
+ // bottom block is too tall to get over
|
|
+ return false;
|
|
+ }
|
|
+ net.minecraft.world.level.block.Block top = level.getBlockState(pos.above()).getBlock();
|
|
+ // only if both blocks have no collision
|
|
+ return !bottom.hasCollision && !top.hasCollision;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public Brain<Villager> getBrain() {
|
|
return (Brain<Villager>) super.getBrain(); // CraftBukkit - decompile error
|
|
@@ -189,7 +275,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
brain.addActivity(Activity.PLAY, VillagerGoalPackages.getPlayPackage(0.5F));
|
|
} else {
|
|
brain.setSchedule(Schedule.VILLAGER_DEFAULT);
|
|
- brain.addActivityWithConditions(Activity.WORK, VillagerGoalPackages.getWorkPackage(villagerprofession, 0.5F), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT)));
|
|
+ brain.addActivityWithConditions(Activity.WORK, VillagerGoalPackages.getWorkPackage(villagerprofession, 0.5F, this.level.purpurConfig.villagerClericsFarmWarts), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT))); // Purpur
|
|
}
|
|
|
|
brain.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(villagerprofession, 0.5F));
|
|
@@ -249,14 +335,29 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
@Override
|
|
protected void customServerAiStep() { mobTick(false); }
|
|
protected void mobTick(boolean inactive) {
|
|
- this.level.getProfiler().push("villagerBrain");
|
|
+ //this.level.getProfiler().push("villagerBrain"); // Purpur
|
|
+ // Purpur start
|
|
+ if (this.level.purpurConfig.villagerLobotomizeEnabled) {
|
|
+ // treat as inactive if lobotomized
|
|
+ inactive = inactive || checkLobotomized();
|
|
+ } else {
|
|
+ // clean up state for API
|
|
+ this.isLobotomized = false;
|
|
+ }
|
|
+ // Purpur end
|
|
// Pufferfish start
|
|
if (!inactive) {
|
|
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
|
|
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
|
|
this.getBrain().tick((ServerLevel) this.level, this); // Paper
|
|
}
|
|
// Pufferfish end
|
|
- this.level.getProfiler().pop();
|
|
+ // Purpur start
|
|
+ else if (this.isLobotomized && shouldRestock()) {
|
|
+ // make sure we restock if needed when lobotomized
|
|
+ restock();
|
|
+ }
|
|
+ // Purpur end
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
if (this.assignProfessionWhenSpawned) {
|
|
this.assignProfessionWhenSpawned = false;
|
|
}
|
|
@@ -312,7 +413,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
if (!itemstack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isSleeping()) {
|
|
if (this.isBaby()) {
|
|
this.setUnhappy();
|
|
- return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level.isClientSide)); // Purpur
|
|
} else {
|
|
boolean flag = this.getOffers().isEmpty();
|
|
|
|
@@ -325,9 +426,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}
|
|
|
|
if (flag) {
|
|
- return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level.isClientSide)); // Purpur
|
|
} else {
|
|
- if (!this.level.isClientSide && !this.offers.isEmpty()) {
|
|
+ if (level.purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur
|
|
+ if (this.level.purpurConfig.villagerAllowTrading && !this.offers.isEmpty()) { // Purpur
|
|
this.startTrading(player);
|
|
}
|
|
|
|
@@ -496,7 +598,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
while (iterator.hasNext()) {
|
|
MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
|
|
|
|
- merchantrecipe.updateDemand();
|
|
+ merchantrecipe.updateDemand(this.level.purpurConfig.villagerMinimumDemand); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -746,7 +848,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
|
|
@Override
|
|
public boolean canBreed() {
|
|
- return this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0;
|
|
+ return this.level.purpurConfig.villagerCanBreed && this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0; // Purpur
|
|
}
|
|
|
|
private boolean hungry() {
|
|
@@ -938,6 +1040,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}
|
|
|
|
public boolean hasFarmSeeds() {
|
|
+ // Purpur start
|
|
+ if (this.level.purpurConfig.villagerClericsFarmWarts && this.getVillagerData().getProfession() == VillagerProfession.CLERIC) {
|
|
+ return this.getInventory().hasAnyOf(ImmutableSet.of(Items.NETHER_WART));
|
|
+ }
|
|
+ // Purpur end
|
|
return this.getInventory().hasAnyOf(ImmutableSet.of(Items.WHEAT_SEEDS, Items.POTATO, Items.CARROT, Items.BEETROOT_SEEDS));
|
|
}
|
|
|
|
@@ -986,6 +1093,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}
|
|
|
|
public void spawnGolemIfNeeded(ServerLevel world, long time, int requiredCount) {
|
|
+ if (world.purpurConfig.villagerSpawnIronGolemRadius > 0 && world.getEntitiesOfClass(net.minecraft.world.entity.animal.IronGolem.class, getBoundingBox().inflate(world.purpurConfig.villagerSpawnIronGolemRadius)).size() > world.purpurConfig.villagerSpawnIronGolemLimit) return; // Purpur
|
|
if (this.wantsToSpawnGolem(time)) {
|
|
AABB axisalignedbb = this.getBoundingBox().inflate(10.0D, 10.0D, 10.0D);
|
|
List<Villager> list = world.getEntitiesOfClass(Villager.class, axisalignedbb);
|
|
@@ -1059,6 +1167,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
|
|
@Override
|
|
public void startSleeping(BlockPos pos) {
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.bedExplodeOnVillagerSleep && this.level.getBlockState(pos).getBlock() instanceof net.minecraft.world.level.block.BedBlock) {
|
|
+ this.level.explode(null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (float) this.level.purpurConfig.bedExplosionPower, this.level.purpurConfig.bedExplosionFire, this.level.purpurConfig.bedExplosionEffect);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
super.startSleeping(pos);
|
|
this.brain.setMemory(MemoryModuleType.LAST_SLEPT, this.level.getGameTime()); // CraftBukkit - decompile error
|
|
this.brain.eraseMemory(MemoryModuleType.WALK_TARGET);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java
|
|
index ac70c2c03241e73943bd517a8c69dd05e0873634..0318663a824d2a9515f867a075d148c3fcb1a907 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java
|
|
@@ -26,7 +26,7 @@ public record VillagerProfession(String name, Predicate<Holder<PoiType>> heldJob
|
|
public static final VillagerProfession ARMORER = register("armorer", PoiTypes.ARMORER, SoundEvents.VILLAGER_WORK_ARMORER);
|
|
public static final VillagerProfession BUTCHER = register("butcher", PoiTypes.BUTCHER, SoundEvents.VILLAGER_WORK_BUTCHER);
|
|
public static final VillagerProfession CARTOGRAPHER = register("cartographer", PoiTypes.CARTOGRAPHER, SoundEvents.VILLAGER_WORK_CARTOGRAPHER);
|
|
- public static final VillagerProfession CLERIC = register("cleric", PoiTypes.CLERIC, SoundEvents.VILLAGER_WORK_CLERIC);
|
|
+ public static final VillagerProfession CLERIC = register("cleric", PoiTypes.CLERIC, ImmutableSet.of(Items.NETHER_WART), ImmutableSet.of(Blocks.SOUL_SAND), SoundEvents.VILLAGER_WORK_CLERIC); // Purpur
|
|
public static final VillagerProfession FARMER = register("farmer", PoiTypes.FARMER, ImmutableSet.of(Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT_SEEDS, Items.BONE_MEAL), ImmutableSet.of(Blocks.FARMLAND), SoundEvents.VILLAGER_WORK_FARMER);
|
|
public static final VillagerProfession FISHERMAN = register("fisherman", PoiTypes.FISHERMAN, SoundEvents.VILLAGER_WORK_FISHERMAN);
|
|
public static final VillagerProfession FLETCHER = register("fletcher", PoiTypes.FLETCHER, SoundEvents.VILLAGER_WORK_FLETCHER);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
|
|
index c9fb50c33ac15fe72bc77167e4647f30942fdc5d..a6a4d5203cb5f35306f8225e56681bc25e06beed 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
|
|
@@ -69,6 +69,43 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
this.setDespawnDelay(48000); // CraftBukkit - set default from MobSpawnerTrader
|
|
}
|
|
|
|
+ // Purpur - start
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return level.purpurConfig.wanderingTraderRidable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return level.purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level.purpurConfig.wanderingTraderRidableInWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isControllable() {
|
|
+ return level.purpurConfig.wanderingTraderControllable;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void initAttributes() {
|
|
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.wanderingTraderMaxHealth);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canBeLeashed(Player player) {
|
|
+ return level.purpurConfig.wanderingTraderCanBeLeashed && !this.isLeashed();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSensitiveToWater() {
|
|
+ return this.level.purpurConfig.wanderingTraderTakeDamageFromWater;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean isAlwaysExperienceDropper() {
|
|
+ return this.level.purpurConfig.wanderingTraderAlwaysDropExp;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
@@ -76,7 +113,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
return this.canDrinkPotion && this.level.isNight() && !entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
|
|
}));
|
|
this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, (entityvillagertrader) -> {
|
|
- return canDrinkMilk && this.level.isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
|
|
+ return level.purpurConfig.milkClearsBeneficialEffects && canDrinkMilk && this.level.isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API // Purpur
|
|
}));
|
|
this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Zombie.class, 8.0F, 0.5D, 0.5D));
|
|
@@ -89,6 +126,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 0.5D));
|
|
this.goalSelector.addGoal(1, new LookAtTradingPlayerGoal(this));
|
|
this.goalSelector.addGoal(2, new WanderingTrader.WanderToPositionGoal(this, 2.0D, 0.35D));
|
|
+ if (level.purpurConfig.wanderingTraderFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false)); // Purpur
|
|
this.goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 0.35D));
|
|
this.goalSelector.addGoal(8, new WaterAvoidingRandomStrollGoal(this, 0.35D));
|
|
this.goalSelector.addGoal(9, new InteractGoal(this, Player.class, 3.0F, 1.0F));
|
|
@@ -116,9 +154,10 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
|
|
}
|
|
|
|
if (this.getOffers().isEmpty()) {
|
|
- return InteractionResult.sidedSuccess(this.level.isClientSide);
|
|
+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level.isClientSide)); // Purpur
|
|
} else {
|
|
- if (!this.level.isClientSide) {
|
|
+ if (level.purpurConfig.wanderingTraderRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur
|
|
+ if (this.level.purpurConfig.wanderingTraderAllowTrading) { // Purpur
|
|
this.setTradingPlayer(player);
|
|
this.openTradingScreen(player, this.getDisplayName(), 1);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
|
|
index 5d199fe497bd852827d3d18fb7566a09e70331a3..6cd8a50289a6404441e9e5e08d82d2ebe14a09cc 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
|
|
@@ -159,7 +159,17 @@ public class WanderingTraderSpawner implements CustomSpawner {
|
|
int k = pos.getX() + this.random.nextInt(range * 2) - range;
|
|
int l = pos.getZ() + this.random.nextInt(range * 2) - range;
|
|
int i1 = world.getHeight(Heightmap.Types.WORLD_SURFACE, k, l);
|
|
- BlockPos blockposition2 = new BlockPos(k, i1, l);
|
|
+ // Purpur start - allow traders to spawn below nether roof
|
|
+ BlockPos.MutableBlockPos blockposition2 = new BlockPos.MutableBlockPos(k, i1, l);
|
|
+ if (world.dimensionType().hasCeiling()) {
|
|
+ do {
|
|
+ blockposition2.relative(net.minecraft.core.Direction.DOWN);
|
|
+ } while (!world.getBlockState(blockposition2).isAir());
|
|
+ do {
|
|
+ blockposition2.relative(net.minecraft.core.Direction.DOWN);
|
|
+ } while (world.getBlockState(blockposition2).isAir() && blockposition2.getY() > 0);
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
if (NaturalSpawner.isSpawnPositionOk(SpawnPlacements.Type.ON_GROUND, world, blockposition2, EntityType.WANDERING_TRADER)) {
|
|
blockposition1 = blockposition2;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
|
|
index 0629c471d38a77c44fc1c86ccdfcb0690f61ca17..7edcb5b86f27d05a0526229262e0d3a3e160362b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
|
|
@@ -186,6 +186,8 @@ public abstract class Player extends LivingEntity {
|
|
public boolean affectsSpawning = true;
|
|
public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET;
|
|
// Paper end
|
|
+ public int sixRowEnderchestSlotCount = -1; // Purpur
|
|
+ public boolean canPortalInstant = false; // Purpur
|
|
|
|
// CraftBukkit start
|
|
public boolean fauxSleeping;
|
|
@@ -197,6 +199,28 @@ public abstract class Player extends LivingEntity {
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ // Purpur start
|
|
+ public int burpDelay = 0;
|
|
+
|
|
+ public abstract void resetLastActionTime();
|
|
+
|
|
+ public void setAfk(boolean afk) {
|
|
+ }
|
|
+
|
|
+ public boolean isAfk() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean processClick(InteractionHand hand) {
|
|
+ Entity vehicle = getRootVehicle();
|
|
+ if (vehicle != null && vehicle.getRider() == this) {
|
|
+ return vehicle.onClick(hand);
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public Player(Level world, BlockPos pos, float yaw, GameProfile gameProfile) {
|
|
super(EntityType.PLAYER, world);
|
|
this.lastItemInMainHand = ItemStack.EMPTY;
|
|
@@ -241,6 +265,12 @@ public abstract class Player extends LivingEntity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (this.burpDelay > 0 && --this.burpDelay == 0) {
|
|
+ this.level.playSound(null, getX(), getY(), getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 1.0F, this.level.random.nextFloat() * 0.1F + 0.9F);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
this.noPhysics = this.isSpectator();
|
|
if (this.isSpectator()) {
|
|
this.onGround = false;
|
|
@@ -344,6 +374,16 @@ public abstract class Player extends LivingEntity {
|
|
this.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 200, 0, false, false, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TURTLE_HELMET); // CraftBukkit
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (this.level.purpurConfig.playerNetheriteFireResistanceDuration > 0 && this.level.getGameTime() % 20 == 0) {
|
|
+ if (itemstack.is(Items.NETHERITE_HELMET)
|
|
+ && this.getItemBySlot(EquipmentSlot.CHEST).is(Items.NETHERITE_CHESTPLATE)
|
|
+ && this.getItemBySlot(EquipmentSlot.LEGS).is(Items.NETHERITE_LEGGINGS)
|
|
+ && this.getItemBySlot(EquipmentSlot.FEET).is(Items.NETHERITE_BOOTS)) {
|
|
+ this.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, this.level.purpurConfig.playerNetheriteFireResistanceDuration, this.level.purpurConfig.playerNetheriteFireResistanceAmplifier, this.level.purpurConfig.playerNetheriteFireResistanceAmbient, this.level.purpurConfig.playerNetheriteFireResistanceShowParticles, this.level.purpurConfig.playerNetheriteFireResistanceShowIcon), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.NETHERITE_ARMOR);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
protected ItemCooldowns createItemCooldowns() {
|
|
@@ -430,7 +470,7 @@ public abstract class Player extends LivingEntity {
|
|
|
|
@Override
|
|
public int getPortalWaitTime() {
|
|
- return this.abilities.invulnerable ? 1 : 80;
|
|
+ return canPortalInstant ? 1 : this.abilities.invulnerable ? this.level.purpurConfig.playerCreativePortalWaitTime : this.level.purpurConfig.playerPortalWaitTime; // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -590,7 +630,7 @@ public abstract class Player extends LivingEntity {
|
|
for (int i = 0; i < list.size(); ++i) {
|
|
Entity entity = (Entity) list.get(i);
|
|
|
|
- if (entity.getType() == EntityType.EXPERIENCE_ORB) {
|
|
+ if (entity.getType() == EntityType.EXPERIENCE_ORB && entity.level.purpurConfig.playerExpPickupDelay >= 0) { // Purpur
|
|
list1.add(entity);
|
|
} else if (!entity.isRemoved()) {
|
|
this.touch(entity);
|
|
@@ -1281,7 +1321,7 @@ public abstract class Player extends LivingEntity {
|
|
flag2 = flag2 && !level.paperConfig().entities.behavior.disablePlayerCrits; // Paper
|
|
flag2 = flag2 && !this.isSprinting();
|
|
if (flag2) {
|
|
- f *= 1.5F;
|
|
+ f *= this.level.purpurConfig.playerCriticalDamageMultiplier; // Purpur
|
|
}
|
|
|
|
f += f1;
|
|
@@ -1950,9 +1990,19 @@ public abstract class Player extends LivingEntity {
|
|
@Override
|
|
public int getExperienceReward() {
|
|
if (!this.level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator()) {
|
|
- int i = this.experienceLevel * 7;
|
|
-
|
|
- return i > 100 ? 100 : i;
|
|
+ // Purpur start
|
|
+ int toDrop;
|
|
+ try {
|
|
+ toDrop = Math.round(((Number) scriptEngine.eval("let expLevel = " + experienceLevel + "; " +
|
|
+ "let expTotal = " + totalExperience + "; " +
|
|
+ "let exp = " + experienceProgress + "; " +
|
|
+ level.purpurConfig.playerDeathExpDropEquation)).floatValue());
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ toDrop = experienceLevel * 7;
|
|
+ }
|
|
+ return Math.min(toDrop, level.purpurConfig.playerDeathExpDropMax);
|
|
+ // Purpur end
|
|
} else {
|
|
return 0;
|
|
}
|
|
@@ -2028,6 +2078,11 @@ public abstract class Player extends LivingEntity {
|
|
return this.inventory.armor;
|
|
}
|
|
|
|
+ @Override
|
|
+ public boolean dismountsUnderwater() {
|
|
+ return !level.purpurConfig.playerRidableInWater;
|
|
+ }
|
|
+
|
|
public boolean setEntityOnShoulder(CompoundTag entityNbt) {
|
|
if (!this.isPassenger() && this.onGround && !this.isInWater() && !this.isInPowderSnow) {
|
|
if (this.getShoulderEntityLeft().isEmpty()) {
|
|
@@ -2311,7 +2366,7 @@ public abstract class Player extends LivingEntity {
|
|
public ItemStack eat(Level world, ItemStack stack) {
|
|
this.getFoodData().eat(stack.getItem(), stack);
|
|
this.awardStat(Stats.ITEM_USED.get(stack.getItem()));
|
|
- world.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F);
|
|
+ // world.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); // Purpur - moved to tick()
|
|
if (this instanceof ServerPlayer) {
|
|
CriteriaTriggers.CONSUME_ITEM.trigger((ServerPlayer) this, stack);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/player/StackedContents.java b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
|
|
index 574ebb3a2fcd0e4e426a8a7ee88d722ed3b9c3f5..842b921799111789b37a34b76644c9217bc85794 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
|
|
@@ -37,8 +37,62 @@ public class StackedContents {
|
|
int i = getStackingIndex(stack);
|
|
int j = Math.min(maxCount, stack.getCount());
|
|
this.put(i, j);
|
|
+ // PaperPR start
|
|
+ if (stack.hasTag()) {
|
|
+ this.put(getExactStackingIndex(stack), j);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+ private static final net.minecraft.core.IdMap<ItemStack> EXACT_MATCHES_ID_MAP = new net.minecraft.core.IdMap<>() {
|
|
+ private final java.util.concurrent.atomic.AtomicInteger idCounter = new java.util.concurrent.atomic.AtomicInteger(BuiltInRegistries.ITEM.size());
|
|
+ private final it.unimi.dsi.fastutil.objects.Object2IntMap<ItemStack> itemstackToId = new it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap<>(new it.unimi.dsi.fastutil.Hash.Strategy<>() {
|
|
+ @Override
|
|
+ public int hashCode(ItemStack o) {
|
|
+ return java.util.Objects.hash(o.getItem(), o.getTag());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean equals(@Nullable ItemStack a, @Nullable ItemStack b) {
|
|
+ if (a == null || b == null) {
|
|
+ return false;
|
|
+ }
|
|
+ return ItemStack.tagMatches(a, b);
|
|
+ }
|
|
+ });
|
|
+ private final it.unimi.dsi.fastutil.ints.Int2ObjectMap<net.minecraft.world.item.ItemStack> idToItemstack = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>();
|
|
+
|
|
+ @Override
|
|
+ public int getId(ItemStack value) {
|
|
+ if (!this.itemstackToId.containsKey(value)) {
|
|
+ final int id = this.idCounter.incrementAndGet();
|
|
+ final ItemStack copy = value.copy();
|
|
+ this.itemstackToId.put(copy, id);
|
|
+ this.idToItemstack.put(id, copy);
|
|
+ return id;
|
|
+ }
|
|
+ return this.itemstackToId.getInt(value);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable ItemStack byId(int index) {
|
|
+ return this.idToItemstack.get(index);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int size() {
|
|
+ return this.itemstackToId.size();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.Iterator<net.minecraft.world.item.ItemStack> iterator() {
|
|
+ return this.idToItemstack.values().iterator();
|
|
}
|
|
+ };
|
|
|
|
+ public static int getExactStackingIndex(ItemStack stack) {
|
|
+ return EXACT_MATCHES_ID_MAP.getId(stack);
|
|
+ // PaperPR end
|
|
}
|
|
|
|
public static int getStackingIndex(ItemStack stack) {
|
|
@@ -80,6 +134,12 @@ public class StackedContents {
|
|
}
|
|
|
|
public static ItemStack fromStackingIndex(int itemId) {
|
|
+ // PaperPR start
|
|
+ if (itemId > BuiltInRegistries.ITEM.size()) {
|
|
+ final ItemStack stack = EXACT_MATCHES_ID_MAP.byId(itemId);
|
|
+ return stack == null ? ItemStack.EMPTY : stack.copy();
|
|
+ }
|
|
+ // PaperPR end
|
|
return itemId == 0 ? ItemStack.EMPTY : new ItemStack(Item.byId(itemId));
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
|
|
index 5d6d26cfe8f0ab68a3145214b3fc126ca7a71a66..c6fddfa199e0c42e0556ed1ad380885a17208e37 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
|
|
@@ -72,6 +72,7 @@ public abstract class AbstractArrow extends Projectile {
|
|
private IntOpenHashSet piercingIgnoreEntityIds;
|
|
@Nullable
|
|
private List<Entity> piercedAndKilledEntities;
|
|
+ public int lootingLevel; // Purpur
|
|
|
|
// Spigot Start
|
|
@Override
|
|
@@ -312,7 +313,7 @@ public abstract class AbstractArrow extends Projectile {
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
|
|
this.setDeltaMovement(vec3d.multiply((double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F)));
|
|
- this.life = 0;
|
|
+ if (this.level.purpurConfig.arrowMovementResetsDespawnCounter) this.life = 0; // Purpur - do not reset despawn counter
|
|
}
|
|
|
|
@Override
|
|
@@ -612,6 +613,12 @@ public abstract class AbstractArrow extends Projectile {
|
|
this.knockback = punch;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void setLootingLevel(int looting) {
|
|
+ this.lootingLevel = looting;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public int getKnockback() {
|
|
return this.knockback;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java b/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java
|
|
index 4daa368881e4fa59a9365d7b3810ae7dc1455fa3..a4041580061b2acd150836a1437df66ebb4a62cb 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java
|
|
@@ -16,20 +16,20 @@ public class LargeFireball extends Fireball {
|
|
|
|
public LargeFireball(EntityType<? extends LargeFireball> type, Level world) {
|
|
super(type, world);
|
|
- isIncendiary = this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit
|
|
+ isIncendiary = this.level.purpurConfig.fireballsBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit // Purpur
|
|
}
|
|
|
|
public LargeFireball(Level world, LivingEntity owner, double velocityX, double velocityY, double velocityZ, int explosionPower) {
|
|
super(EntityType.FIREBALL, owner, velocityX, velocityY, velocityZ, world);
|
|
this.explosionPower = explosionPower;
|
|
- isIncendiary = this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit
|
|
+ isIncendiary = this.level.purpurConfig.fireballsBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void onHit(HitResult hitResult) {
|
|
super.onHit(hitResult);
|
|
if (!this.level.isClientSide) {
|
|
- boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ boolean flag = this.level.purpurConfig.fireballsBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur
|
|
|
|
// CraftBukkit start - fire ExplosionPrimeEvent
|
|
ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity());
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
|
|
index c4f4a26e016eea744f587461af80461074d48303..10b109de5abc015b61a896d363ad37a006dff554 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
|
|
@@ -26,6 +26,12 @@ public class LlamaSpit extends Projectile {
|
|
this.setPos(owner.getX() - (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(owner.yBodyRot * 0.017453292F), owner.getEyeY() - 0.10000000149011612D, owner.getZ() + (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(owner.yBodyRot * 0.017453292F));
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public void super_tick() {
|
|
+ super.tick();
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
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 85260fd93ca8d5ffd0ba7a98f1d47093d58f0f87..505a60605ff2282f2c460c81464d60a433f41eda 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
@@ -318,6 +318,6 @@ public abstract class Projectile extends Entity implements TraceableEntity {
|
|
public boolean mayInteract(Level world, BlockPos pos) {
|
|
Entity entity = this.getOwner();
|
|
|
|
- return entity instanceof Player ? entity.mayInteract(world, pos) : entity == null || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ return entity instanceof Player ? entity.mayInteract(world, pos) : entity == null || world.purpurConfig.projectilesBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java b/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java
|
|
index b9e4955fecabbad8d6762f3d933ea1402e932d9b..895c501f8579799139239701703078ef060cd481 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java
|
|
@@ -24,7 +24,7 @@ public class SmallFireball extends Fireball {
|
|
super(EntityType.SMALL_FIREBALL, owner, velocityX, velocityY, velocityZ, world);
|
|
// CraftBukkit start
|
|
if (this.getOwner() != null && this.getOwner() instanceof Mob) {
|
|
- isIncendiary = this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ isIncendiary = this.level.purpurConfig.fireballsBypassMobGriefing || this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur
|
|
}
|
|
// CraftBukkit end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java
|
|
index 6cded52e4627c2b6073fa221fc6d6583f1b2a96d..9a84e8cc1d1b2803a061fe9ef6297c9c4aea34c5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java
|
|
@@ -53,10 +53,40 @@ public class Snowball extends ThrowableItemProjectile {
|
|
protected void onHitEntity(EntityHitResult entityHitResult) {
|
|
super.onHitEntity(entityHitResult);
|
|
Entity entity = entityHitResult.getEntity();
|
|
- int i = entity instanceof Blaze ? 3 : 0;
|
|
+ int i = entity.level.purpurConfig.snowballDamage >= 0 ? entity.level.purpurConfig.snowballDamage : entity instanceof Blaze ? 3 : 0; // Purpur
|
|
entity.hurt(this.damageSources().thrown(this, this.getOwner()), (float)i);
|
|
}
|
|
|
|
+ // Purpur start - borrowed and modified code from ThrownPotion#onHitBlock and ThrownPotion#dowseFire
|
|
+ @Override
|
|
+ protected void onHitBlock(net.minecraft.world.phys.BlockHitResult blockHitResult) {
|
|
+ super.onHitBlock(blockHitResult);
|
|
+
|
|
+ if (!this.level.isClientSide) {
|
|
+ net.minecraft.core.BlockPos blockposition = blockHitResult.getBlockPos();
|
|
+ net.minecraft.core.BlockPos blockposition1 = blockposition.relative(blockHitResult.getDirection());
|
|
+
|
|
+ net.minecraft.world.level.block.state.BlockState iblockdata = this.level.getBlockState(blockposition);
|
|
+
|
|
+ if (this.level.purpurConfig.snowballExtinguishesFire && this.level.getBlockState(blockposition1).is(net.minecraft.world.level.block.Blocks.FIRE)) {
|
|
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition1, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState()).isCancelled()) {
|
|
+ this.level.removeBlock(blockposition1, false);
|
|
+ }
|
|
+ } else if (this.level.purpurConfig.snowballExtinguishesCandles && net.minecraft.world.level.block.AbstractCandleBlock.isLit(iblockdata)) {
|
|
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.setValue(net.minecraft.world.level.block.AbstractCandleBlock.LIT, false)).isCancelled()) {
|
|
+ net.minecraft.world.level.block.AbstractCandleBlock.extinguish(null, iblockdata, this.level, blockposition);
|
|
+ }
|
|
+ } else if (this.level.purpurConfig.snowballExtinguishesCampfires && net.minecraft.world.level.block.CampfireBlock.isLitCampfire(iblockdata)) {
|
|
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false)).isCancelled()) {
|
|
+ this.level.levelEvent(null, 1009, blockposition, 0);
|
|
+ net.minecraft.world.level.block.CampfireBlock.dowse(this.getOwner(), this.level, blockposition, iblockdata);
|
|
+ this.level.setBlockAndUpdate(blockposition, iblockdata.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected void onHit(HitResult hitResult) {
|
|
super.onHit(hitResult);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
index 39ab9a283d856ba8d578d1378285758e32a24cf0..f35547c6b5951bc6eb4df74b2a94496fd20d69b5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
@@ -68,10 +68,11 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
|
|
Bukkit.getPluginManager().callEvent(teleEvent);
|
|
|
|
if (!teleEvent.isCancelled() && entityplayer.connection.isAcceptingMessages()) {
|
|
- if (this.random.nextFloat() < 0.05F && this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
|
|
+ if (this.random.nextFloat() < this.level.purpurConfig.enderPearlEndermiteChance && this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { // Purpur
|
|
Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(this.level);
|
|
|
|
if (entityendermite != null) {
|
|
+ entityendermite.setPlayerSpawned(true); // Purpur
|
|
entityendermite.moveTo(entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot());
|
|
this.level.addFreshEntity(entityendermite, CreatureSpawnEvent.SpawnReason.ENDER_PEARL);
|
|
}
|
|
@@ -84,7 +85,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
|
|
entityplayer.connection.teleport(teleEvent.getTo());
|
|
entity.resetFallDistance();
|
|
CraftEventFactory.entityDamage = this;
|
|
- entity.hurt(this.damageSources().fall(), 5.0F);
|
|
+ entity.hurt(this.damageSources().fall(), this.level.purpurConfig.enderPearlDamage); // Purpur
|
|
CraftEventFactory.entityDamage = null;
|
|
}
|
|
// CraftBukkit end
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java
|
|
index db151bf624095014c99d78b4f6748d2c3792abea..fb10fc5a5aa3b6d6220694041778bfd39ffa1cb8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java
|
|
@@ -60,7 +60,7 @@ public class ThrownTrident extends AbstractArrow {
|
|
Entity entity = this.getOwner();
|
|
byte b0 = (Byte) this.entityData.get(ThrownTrident.ID_LOYALTY);
|
|
|
|
- if (b0 > 0 && (this.dealtDamage || this.isNoPhysics()) && entity != null) {
|
|
+ if (b0 > 0 && (this.dealtDamage || this.isNoPhysics() || (level.purpurConfig.tridentLoyaltyVoidReturnHeight < 0.0D && getY() < level.purpurConfig.tridentLoyaltyVoidReturnHeight)) && entity != null) { // Purpur
|
|
if (!this.isAcceptibleReturnOwner()) {
|
|
if (!this.level.isClientSide && this.pickup == AbstractArrow.Pickup.ALLOWED) {
|
|
this.spawnAtLocation(this.getPickupItem(), 0.1F);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
|
|
index 093a00e52062868b4fbf358b307513d0f599f69d..ba753735f3cbb2cb3d0a491d1bd94a04f83b123d 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
|
|
@@ -95,7 +95,7 @@ public class WitherSkull extends AbstractHurtingProjectile {
|
|
if (!this.level.isClientSide) {
|
|
// CraftBukkit start
|
|
// this.level.explode(this, this.getX(), this.getY(), this.getZ(), 1.0F, false, World.a.MOB);
|
|
- ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false);
|
|
+ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), this.level.purpurConfig.witherExplosionRadius, false); // Purpur
|
|
this.level.getCraftServer().getPluginManager().callEvent(event);
|
|
|
|
if (!event.isCancelled()) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
|
index 99e9d46d42ddd0b451c6aeb847f1b295ebe5c697..f7b03dc1d5316aeb760a52d6f6c50e8aae5f978e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
|
@@ -319,7 +319,7 @@ public abstract class Raider extends PatrollingMonster {
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
- if (!this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items
|
|
+ if ((!this.mob.level.purpurConfig.pillagerBypassMobGriefing && !this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items // Purpur
|
|
Raid raid = this.mob.getCurrentRaid();
|
|
|
|
if (this.mob.hasActiveRaid() && !this.mob.getCurrentRaid().isOver() && this.mob.canBeLeader() && !ItemStack.matches(this.mob.getItemBySlot(EquipmentSlot.HEAD), Raid.getLeaderBannerInstance())) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raids.java b/src/main/java/net/minecraft/world/entity/raid/Raids.java
|
|
index fabce3bc592b1b172b227395a07febdbb66ec3c9..df48bcc8f329e3855bb7426bdfe0e3c72af53bea 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/raid/Raids.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/raid/Raids.java
|
|
@@ -28,6 +28,7 @@ import net.minecraft.world.phys.Vec3;
|
|
public class Raids extends SavedData {
|
|
|
|
private static final String RAID_FILE_ID = "raids";
|
|
+ public final Map<java.util.UUID, Integer> playerCooldowns = Maps.newHashMap();
|
|
public final Map<Integer, Raid> raidMap = Maps.newHashMap();
|
|
private final ServerLevel level;
|
|
private int nextAvailableID;
|
|
@@ -45,6 +46,17 @@ public class Raids extends SavedData {
|
|
|
|
public void tick() {
|
|
++this.tick;
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.raidCooldownSeconds != 0 && this.tick % 20 == 0) {
|
|
+ com.google.common.collect.ImmutableMap.copyOf(playerCooldowns).forEach((uuid, i) -> {
|
|
+ if (i < 1) {
|
|
+ playerCooldowns.remove(uuid);
|
|
+ } else {
|
|
+ playerCooldowns.put(uuid, i - 1);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ // Purpur end
|
|
Iterator iterator = this.raidMap.values().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -129,11 +141,13 @@ public class Raids extends SavedData {
|
|
}
|
|
|
|
if (flag) {
|
|
+ if (level.purpurConfig.raidCooldownSeconds != 0 && playerCooldowns.containsKey(player.getUUID())) return null; // Purpur
|
|
// CraftBukkit start
|
|
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(raid, player)) {
|
|
player.removeEffect(MobEffects.BAD_OMEN);
|
|
return null;
|
|
}
|
|
+ if (level.purpurConfig.raidCooldownSeconds != 0) playerCooldowns.put(player.getUUID(), level.purpurConfig.raidCooldownSeconds); // Purpur
|
|
|
|
if (!this.raidMap.containsKey(raid.getId())) {
|
|
this.raidMap.put(raid.getId(), raid);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
|
|
index ee4f924afe15c9a4d96af7a55b357076c7b28501..ddcd7ada9f4bf5653bd8bf7c6cdb35d7243367d9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
|
|
@@ -107,11 +107,13 @@ public abstract class AbstractMinecart extends Entity {
|
|
private double flyingY = 0.949999988079071D; // Paper - restore vanilla precision
|
|
private double flyingZ = 0.949999988079071D; // Paper - restore vanilla precision
|
|
public double maxSpeed = 0.4D;
|
|
+ public double storedMaxSpeed; // Purpur
|
|
// CraftBukkit end
|
|
|
|
protected AbstractMinecart(EntityType<?> type, Level world) {
|
|
super(type, world);
|
|
this.blocksBuilding = true;
|
|
+ if (world != null) maxSpeed = storedMaxSpeed = world.purpurConfig.minecartMaxSpeed; // Purpur
|
|
}
|
|
|
|
protected AbstractMinecart(EntityType<?> type, Level world, double x, double y, double z) {
|
|
@@ -334,6 +336,12 @@ public abstract class AbstractMinecart extends Entity {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // Purpur start
|
|
+ if (storedMaxSpeed != level.purpurConfig.minecartMaxSpeed) {
|
|
+ maxSpeed = storedMaxSpeed = level.purpurConfig.minecartMaxSpeed;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// CraftBukkit start
|
|
double prevX = this.getX();
|
|
double prevY = this.getY();
|
|
@@ -497,16 +505,63 @@ public abstract class AbstractMinecart extends Entity {
|
|
|
|
public void activateMinecart(int x, int y, int z, boolean powered) {}
|
|
|
|
+ // Purpur start
|
|
+ private Double lastSpeed;
|
|
+
|
|
+ public double getControllableSpeed() {
|
|
+ BlockPos pos = new BlockPos(this);
|
|
+ Block block = level.getBlockState(pos).getBlock();
|
|
+ if (!block.material.isSolid()) {
|
|
+ block = level.getBlockState(pos.relative(Direction.DOWN)).getBlock();
|
|
+ }
|
|
+ Double speed = level.purpurConfig.minecartControllableBlockSpeeds.get(block);
|
|
+ if (!block.material.isSolid()) {
|
|
+ speed = lastSpeed;
|
|
+ }
|
|
+ if (speed == null) {
|
|
+ speed = level.purpurConfig.minecartControllableBaseSpeed;
|
|
+ }
|
|
+ return lastSpeed = speed;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
protected void comeOffTrack() {
|
|
double d0 = this.getMaxSpeed();
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
|
|
this.setDeltaMovement(Mth.clamp(vec3d.x, -d0, d0), vec3d.y, Mth.clamp(vec3d.z, -d0, d0));
|
|
+
|
|
+ // Purpur start
|
|
+ if (level.purpurConfig.minecartControllable && !isInWater() && !isInLava() && !passengers.isEmpty()) {
|
|
+ Entity passenger = passengers.get(0);
|
|
+ if (passenger instanceof Player) {
|
|
+ Player player = (Player) passenger;
|
|
+ if (player.jumping && this.onGround) {
|
|
+ setDeltaMovement(new Vec3(getDeltaMovement().x, level.purpurConfig.minecartControllableHopBoost, getDeltaMovement().z));
|
|
+ }
|
|
+ if (player.zza != 0.0F) {
|
|
+ Vector velocity = player.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(getControllableSpeed());
|
|
+ if (player.zza < 0.0) {
|
|
+ velocity.multiply(-0.5);
|
|
+ }
|
|
+ setDeltaMovement(new Vec3(velocity.getX(), getDeltaMovement().y, velocity.getZ()));
|
|
+ }
|
|
+ this.setYRot(passenger.getYRot() - 90);
|
|
+ maxUpStep = level.purpurConfig.minecartControllableStepHeight;
|
|
+ } else {
|
|
+ maxUpStep = 0.0F;
|
|
+ }
|
|
+ } else {
|
|
+ maxUpStep = 0.0F;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (this.onGround) {
|
|
// CraftBukkit start - replace magic numbers with our variables
|
|
this.setDeltaMovement(new Vec3(this.getDeltaMovement().x * this.derailedX, this.getDeltaMovement().y * this.derailedY, this.getDeltaMovement().z * this.derailedZ));
|
|
// CraftBukkit end
|
|
}
|
|
+ else if (level.purpurConfig.minecartControllable) setDeltaMovement(new Vec3(getDeltaMovement().x * derailedX, getDeltaMovement().y, getDeltaMovement().z * derailedZ)); // Purpur
|
|
|
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
|
if (!this.onGround) {
|
|
@@ -668,7 +723,7 @@ public abstract class AbstractMinecart extends Entity {
|
|
if (d18 > 0.01D) {
|
|
double d20 = 0.06D;
|
|
|
|
- this.setDeltaMovement(vec3d4.add(vec3d4.x / d18 * 0.06D, 0.0D, vec3d4.z / d18 * 0.06D));
|
|
+ this.setDeltaMovement(vec3d4.add(vec3d4.x / d18 * this.level.purpurConfig.poweredRailBoostModifier, 0.0D, vec3d4.z / d18 * this.level.purpurConfig.poweredRailBoostModifier)); // Purpur
|
|
} else {
|
|
Vec3 vec3d5 = this.getDeltaMovement();
|
|
double d21 = vec3d5.x;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
|
|
index 12e3209c5246ede89daaf8455fe70b4a517e12f6..06421017e3a7a0511c253e2ad4a028b0c156c9a8 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
|
|
@@ -223,7 +223,13 @@ public class Boat extends Entity implements VariantHolder<Boat.Type> {
|
|
}
|
|
|
|
protected void destroy(DamageSource source) {
|
|
- this.spawnAtLocation((ItemLike) this.getDropItem());
|
|
+ // Purpur start
|
|
+ final ItemStack boat = new ItemStack(this.getDropItem());
|
|
+ if (this.level.purpurConfig.persistentDroppableEntityDisplayNames && this.hasCustomName()) {
|
|
+ boat.setHoverName(this.getCustomName());
|
|
+ }
|
|
+ this.spawnAtLocation(boat);
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -541,6 +547,7 @@ public class Boat extends Entity implements VariantHolder<Boat.Type> {
|
|
|
|
if (f > 0.0F) {
|
|
this.landFriction = f;
|
|
+ if (level.purpurConfig.boatEjectPlayersOnLand) ejectPassengers(); // Purpur
|
|
return Boat.Status.ON_LAND;
|
|
} else {
|
|
return Boat.Status.IN_AIR;
|
|
diff --git a/src/main/java/net/minecraft/world/food/FoodData.java b/src/main/java/net/minecraft/world/food/FoodData.java
|
|
index 4a2dcf9bd83dd3fdff43483f887f4f58dc4715cd..3d2e3c7dd3bf5c8f483a933e9f6868586c0d3911 100644
|
|
--- a/src/main/java/net/minecraft/world/food/FoodData.java
|
|
+++ b/src/main/java/net/minecraft/world/food/FoodData.java
|
|
@@ -33,8 +33,10 @@ public class FoodData {
|
|
// CraftBukkit end
|
|
|
|
public void eat(int food, float saturationModifier) {
|
|
+ int oldValue = this.foodLevel; // Purpur
|
|
this.foodLevel = Math.min(food + this.foodLevel, 20);
|
|
this.saturationLevel = Math.min(this.saturationLevel + (float) food * saturationModifier * 2.0F, (float) this.foodLevel);
|
|
+ if (this.entityhuman.level.purpurConfig.playerBurpWhenFull && this.foodLevel == 20 && oldValue < 20) this.entityhuman.burpDelay = this.entityhuman.level.purpurConfig.playerBurpDelay; // Purpur
|
|
}
|
|
|
|
public void eat(Item item, ItemStack stack) {
|
|
@@ -100,7 +102,7 @@ public class FoodData {
|
|
++this.tickTimer;
|
|
if (this.tickTimer >= this.starvationRate) { // CraftBukkit - add regen rate manipulation
|
|
if (player.getHealth() > 10.0F || enumdifficulty == Difficulty.HARD || player.getHealth() > 1.0F && enumdifficulty == Difficulty.NORMAL) {
|
|
- player.hurt(player.damageSources().starve(), 1.0F);
|
|
+ player.hurt(player.damageSources().starve(), player.level.purpurConfig.hungerStarvationDamage); // Purpur
|
|
}
|
|
|
|
this.tickTimer = 0;
|
|
diff --git a/src/main/java/net/minecraft/world/food/FoodProperties.java b/src/main/java/net/minecraft/world/food/FoodProperties.java
|
|
index 9967ba762567631f2bdb1e4f8fe16a13ea927b46..6c945ae8fe8b1517e312c688f829fab41f12d9f4 100644
|
|
--- a/src/main/java/net/minecraft/world/food/FoodProperties.java
|
|
+++ b/src/main/java/net/minecraft/world/food/FoodProperties.java
|
|
@@ -2,15 +2,22 @@ package net.minecraft.world.food;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.mojang.datafixers.util.Pair;
|
|
+
|
|
+import java.util.ArrayList;
|
|
import java.util.List;
|
|
import net.minecraft.world.effect.MobEffectInstance;
|
|
|
|
public class FoodProperties {
|
|
- private final int nutrition;
|
|
- private final float saturationModifier;
|
|
- private final boolean isMeat;
|
|
- private final boolean canAlwaysEat;
|
|
- private final boolean fastFood;
|
|
+ // Purpur start
|
|
+ private int nutrition; public void setNutrition(int nutrition) { this.nutrition = nutrition; }
|
|
+ private float saturationModifier; public void setSaturationModifier(float saturation) { this.saturationModifier = saturation; }
|
|
+ private boolean isMeat; public void setIsMeat(boolean isMeat) { this.isMeat = isMeat; }
|
|
+ private boolean canAlwaysEat; public void setCanAlwaysEat(boolean canAlwaysEat) { this.canAlwaysEat = canAlwaysEat; }
|
|
+ private boolean fastFood; public void setFastFood(boolean isFastFood) { this.fastFood = isFastFood; }
|
|
+ public FoodProperties copy() {
|
|
+ return new FoodProperties(this.nutrition, this.saturationModifier, this.isMeat, this.canAlwaysEat, this.fastFood, new ArrayList<>(this.effects));
|
|
+ }
|
|
+ // Purpur end
|
|
private final List<Pair<MobEffectInstance, Float>> effects;
|
|
|
|
FoodProperties(int hunger, float saturationModifier, boolean meat, boolean alwaysEdible, boolean snack, List<Pair<MobEffectInstance, Float>> statusEffects) {
|
|
diff --git a/src/main/java/net/minecraft/world/food/Foods.java b/src/main/java/net/minecraft/world/food/Foods.java
|
|
index b16d9e2eaa589f19c563ee70b1a56d67dbcdecb0..71beab673f04cd051c46ea37f8c847316885d38d 100644
|
|
--- a/src/main/java/net/minecraft/world/food/Foods.java
|
|
+++ b/src/main/java/net/minecraft/world/food/Foods.java
|
|
@@ -4,6 +4,9 @@ import net.minecraft.world.effect.MobEffectInstance;
|
|
import net.minecraft.world.effect.MobEffects;
|
|
|
|
public class Foods {
|
|
+ public static final java.util.Map<String, FoodProperties> ALL_PROPERTIES = new java.util.HashMap<>(); // Purpur
|
|
+ public static final java.util.Map<String, FoodProperties> DEFAULT_PROPERTIES = new java.util.HashMap<>(); // Purpur
|
|
+
|
|
public static final FoodProperties APPLE = (new FoodProperties.Builder()).nutrition(4).saturationMod(0.3F).build();
|
|
public static final FoodProperties BAKED_POTATO = (new FoodProperties.Builder()).nutrition(5).saturationMod(0.6F).build();
|
|
public static final FoodProperties BEEF = (new FoodProperties.Builder()).nutrition(3).saturationMod(0.3F).meat().build();
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
index c84908095a93d42826b21bf5f3490410fb0a5708..20b328704981c088597359fe18c1d67c339c1c0f 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
@@ -76,6 +76,7 @@ public abstract class AbstractContainerMenu {
|
|
@Nullable
|
|
private ContainerSynchronizer synchronizer;
|
|
private boolean suppressRemoteUpdates;
|
|
+ @javax.annotation.Nullable protected ItemStack activeQuickItem = null; // Purpur
|
|
|
|
// CraftBukkit start
|
|
public boolean checkReachable = true;
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java
|
|
index 8ae78ae54d87ea7789df754311fa0e8aade0ce91..35479019fb846573f4e2eb5902f3ebe898c4d61f 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java
|
|
@@ -145,7 +145,13 @@ public abstract class AbstractFurnaceMenu extends RecipeBookMenu<Container> {
|
|
} else if (slot != 1 && slot != 0) {
|
|
if (this.canSmelt(itemstack1)) {
|
|
if (!this.moveItemStackTo(itemstack1, 0, 1, false)) {
|
|
- return ItemStack.EMPTY;
|
|
+ // Purpur start - fix #625
|
|
+ if (this.isFuel(itemstack1)) {
|
|
+ if (!this.moveItemStackTo(itemstack1, 1, 2, false)) {
|
|
+ return ItemStack.EMPTY;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
} else if (this.isFuel(itemstack1)) {
|
|
if (!this.moveItemStackTo(itemstack1, 1, 2, false)) {
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
|
index b7a2295290227045e6426ee0f71707185d95b943..7ade5cd93b3d7b7259bf3246a8b74b0b31407817 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
|
@@ -21,6 +21,13 @@ import org.slf4j.Logger;
|
|
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
|
|
// CraftBukkit end
|
|
|
|
+// Purpur start
|
|
+import net.minecraft.nbt.IntTag;
|
|
+import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket;
|
|
+import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+// Purpur end
|
|
+
|
|
public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
public static final int INPUT_SLOT = 0;
|
|
@@ -48,6 +55,8 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
public int maximumRepairCost = 40;
|
|
private CraftInventoryView bukkitEntity;
|
|
// CraftBukkit end
|
|
+ public boolean bypassCost = false; // Purpur
|
|
+ public boolean canDoUnsafeEnchants = false; // Purpur
|
|
|
|
public AnvilMenu(int syncId, Inventory inventory) {
|
|
this(syncId, inventory, ContainerLevelAccess.NULL);
|
|
@@ -75,12 +84,15 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
@Override
|
|
protected boolean mayPickup(Player player, boolean present) {
|
|
- return (player.getAbilities().instabuild || player.experienceLevel >= this.cost.get()) && this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST && present; // CraftBukkit - allow cost 0 like a free item
|
|
+ return (player.getAbilities().instabuild || player.experienceLevel >= this.cost.get()) && (bypassCost || this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST) && present; // CraftBukkit - allow cost 0 like a free item // Purpur
|
|
}
|
|
|
|
@Override
|
|
protected void onTake(Player player, ItemStack stack) {
|
|
+ ItemStack itemstack = activeQuickItem == null ? stack : activeQuickItem; // Purpur
|
|
+ if (org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)).callEvent(); // Purpur
|
|
if (!player.getAbilities().instabuild) {
|
|
+ if (bypassCost) ((ServerPlayer) player).lastSentExp = -1; else // Purpur
|
|
player.giveExperienceLevels(-this.cost.get());
|
|
}
|
|
|
|
@@ -131,6 +143,12 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
|
|
@Override
|
|
public void createResult() {
|
|
+ // Purpur start
|
|
+ bypassCost = false;
|
|
+ canDoUnsafeEnchants = false;
|
|
+ if (org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent(getBukkitView()).callEvent();
|
|
+ // Purpur end
|
|
+
|
|
ItemStack itemstack = this.inputSlots.getItem(0);
|
|
|
|
this.cost.set(1);
|
|
@@ -207,7 +225,8 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
int i2 = (Integer) map1.get(enchantment);
|
|
|
|
i2 = l1 == i2 ? i2 + 1 : Math.max(i2, l1);
|
|
- boolean flag3 = enchantment.canEnchant(itemstack);
|
|
+ boolean flag3 = canDoUnsafeEnchants || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants && org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants) || enchantment.canEnchant(itemstack); // Purpur
|
|
+ boolean flag4 = true; // Purpur
|
|
|
|
if (this.player.getAbilities().instabuild || itemstack.is(Items.ENCHANTED_BOOK)) {
|
|
flag3 = true;
|
|
@@ -219,16 +238,16 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
Enchantment enchantment1 = (Enchantment) iterator1.next();
|
|
|
|
if (enchantment1 != enchantment && !enchantment.isCompatibleWith(enchantment1)) {
|
|
- flag3 = false;
|
|
+ flag4 = canDoUnsafeEnchants || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants && org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants); // Purpur flag3 -> flag4
|
|
++i;
|
|
}
|
|
}
|
|
|
|
- if (!flag3) {
|
|
+ if (!flag3 || !flag4) { // Purpur
|
|
flag2 = true;
|
|
} else {
|
|
flag1 = true;
|
|
- if (i2 > enchantment.getMaxLevel()) {
|
|
+ if ((!org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants || !org.purpurmc.purpur.PurpurConfig.allowHigherEnchantsLevels) && i2 > enchantment.getMaxLevel()) { // Purpur
|
|
i2 = enchantment.getMaxLevel();
|
|
}
|
|
|
|
@@ -278,6 +297,54 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
} else if (!this.itemName.equals(itemstack.getHoverName().getString())) {
|
|
b1 = 1;
|
|
i += b1;
|
|
+ // Purpur start
|
|
+ if (this.player != null) {
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity player = this.player.getBukkitEntity();
|
|
+ String name = this.itemName;
|
|
+ boolean removeItalics = false;
|
|
+ if (player.hasPermission("purpur.anvil.remove_italics")) {
|
|
+ if (name.startsWith("&r")) {
|
|
+ name = name.substring(2);
|
|
+ removeItalics = true;
|
|
+ } else if (name.startsWith("<r>")) {
|
|
+ name = name.substring(3);
|
|
+ removeItalics = true;
|
|
+ } else if (name.startsWith("<reset>")) {
|
|
+ name = name.substring(7);
|
|
+ removeItalics = true;
|
|
+ }
|
|
+ }
|
|
+ if (this.player.level.purpurConfig.anvilAllowColors) {
|
|
+ if (player.hasPermission("purpur.anvil.color")) {
|
|
+ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([0-9a-fr])").matcher(name);
|
|
+ while (matcher.find()) {
|
|
+ String match = matcher.group(1);
|
|
+ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT));
|
|
+ }
|
|
+ //name = name.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1");
|
|
+ }
|
|
+ if (player.hasPermission("purpur.anvil.format")) {
|
|
+ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([k-or])").matcher(name);
|
|
+ while (matcher.find()) {
|
|
+ String match = matcher.group(1);
|
|
+ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT));
|
|
+ }
|
|
+ //name = name.replaceAll("(?i)&([l-or])", "\u00a7$1");
|
|
+ }
|
|
+ }
|
|
+ net.kyori.adventure.text.Component component;
|
|
+ if (this.player.level.purpurConfig.anvilColorsUseMiniMessage && player.hasPermission("purpur.anvil.minimessage")) {
|
|
+ component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.bukkit.ChatColor.stripColor(name));
|
|
+ } else {
|
|
+ component = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(name);
|
|
+ }
|
|
+ if (removeItalics) {
|
|
+ component = component.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false);
|
|
+ }
|
|
+ itemstack1.setHoverName(io.papermc.paper.adventure.PaperAdventure.asVanilla(component));
|
|
+ }
|
|
+ else
|
|
+ // Purpur end
|
|
itemstack1.setHoverName(Component.literal(this.itemName));
|
|
}
|
|
|
|
@@ -290,6 +357,13 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
this.cost.set(this.maximumRepairCost - 1); // CraftBukkit
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (bypassCost && cost.get() >= maximumRepairCost) {
|
|
+ itemstack.addTagElement("Purpur.realCost", IntTag.valueOf(cost.get()));
|
|
+ cost.set(maximumRepairCost - 1);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
if (this.cost.get() >= this.maximumRepairCost && !this.player.getAbilities().instabuild) { // CraftBukkit
|
|
itemstack1 = ItemStack.EMPTY;
|
|
}
|
|
@@ -312,11 +386,17 @@ public class AnvilMenu extends ItemCombinerMenu {
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), itemstack1); // CraftBukkit
|
|
sendAllDataToRemote(); // CraftBukkit - SPIGOT-6686: Always send completed inventory to stay in sync with client
|
|
this.broadcastChanges();
|
|
+ // Purpur start
|
|
+ if ((canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants) && itemstack1 != ItemStack.EMPTY) {
|
|
+ ((ServerPlayer) player).connection.send(new ClientboundContainerSetSlotPacket(containerId, incrementStateId(), 2, itemstack1));
|
|
+ ((ServerPlayer) player).connection.send(new ClientboundContainerSetDataPacket(containerId, 0, cost.get()));
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
|
|
public static int calculateIncreasedRepairCost(int cost) {
|
|
- return cost * 2 + 1;
|
|
+ return org.purpurmc.purpur.PurpurConfig.anvilCumulativeCost ? cost * 2 + 1 : 0;
|
|
}
|
|
|
|
public void setItemName(String newItemName) {
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/ChestMenu.java b/src/main/java/net/minecraft/world/inventory/ChestMenu.java
|
|
index 0dbfd23bbfc6ad203f048142f8c90ef741849fe1..9a80427d2bb470b6b1638e59aba57216676dcbd2 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/ChestMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/ChestMenu.java
|
|
@@ -67,10 +67,30 @@ public class ChestMenu extends AbstractContainerMenu {
|
|
return new ChestMenu(MenuType.GENERIC_9x6, syncId, playerInventory, 6);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static ChestMenu oneRow(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x1, syncId, playerInventory, inventory, 1);
|
|
+ }
|
|
+
|
|
+ public static ChestMenu twoRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x2, syncId, playerInventory, inventory, 2);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static ChestMenu threeRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
return new ChestMenu(MenuType.GENERIC_9x3, syncId, playerInventory, inventory, 3);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static ChestMenu fourRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x4, syncId, playerInventory, inventory, 4);
|
|
+ }
|
|
+
|
|
+ public static ChestMenu fiveRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
+ return new ChestMenu(MenuType.GENERIC_9x5, syncId, playerInventory, inventory, 5);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static ChestMenu sixRows(int syncId, Inventory playerInventory, Container inventory) {
|
|
return new ChestMenu(MenuType.GENERIC_9x6, syncId, playerInventory, inventory, 6);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
|
|
index c2fc00509bf3690d359928e8d352d4b3c2ca1491..69ae671be07b1928e778399551991777829e432a 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
|
|
@@ -38,6 +38,12 @@ import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
|
|
import org.bukkit.entity.Player;
|
|
// CraftBukkit end
|
|
|
|
+// Purpur start
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.entity.EnchantmentTableBlockEntity;
|
|
+import org.bukkit.craftbukkit.entity.CraftHumanEntity;
|
|
+// Purpur end
|
|
+
|
|
public class EnchantmentMenu extends AbstractContainerMenu {
|
|
|
|
private final Container enchantSlots;
|
|
@@ -71,6 +77,22 @@ public class EnchantmentMenu extends AbstractContainerMenu {
|
|
return context.getLocation();
|
|
}
|
|
// CraftBukkit end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void onClose(CraftHumanEntity who) {
|
|
+ super.onClose(who);
|
|
+
|
|
+ if (who.getHandle().getLevel().purpurConfig.enchantmentTableLapisPersists) {
|
|
+ access.execute((level, pos) -> {
|
|
+ BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
+ if (blockEntity instanceof EnchantmentTableBlockEntity enchantmentTable) {
|
|
+ enchantmentTable.setLapis(this.getItem(1).getCount());
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
};
|
|
this.random = RandomSource.create();
|
|
this.enchantmentSeed = DataSlot.standalone();
|
|
@@ -96,6 +118,17 @@ public class EnchantmentMenu extends AbstractContainerMenu {
|
|
}
|
|
});
|
|
|
|
+ // Purpur start
|
|
+ access.execute((level, pos) -> {
|
|
+ if (level.purpurConfig.enchantmentTableLapisPersists) {
|
|
+ BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
+ if (blockEntity instanceof EnchantmentTableBlockEntity enchantmentTable) {
|
|
+ this.getSlot(1).set(new ItemStack(Items.LAPIS_LAZULI, enchantmentTable.getLapis()));
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
+
|
|
int j;
|
|
|
|
for (j = 0; j < 3; ++j) {
|
|
@@ -338,6 +371,7 @@ public class EnchantmentMenu extends AbstractContainerMenu {
|
|
public void removed(net.minecraft.world.entity.player.Player player) {
|
|
super.removed(player);
|
|
this.access.execute((world, blockposition) -> {
|
|
+ if (world.purpurConfig.enchantmentTableLapisPersists) this.getSlot(1).set(ItemStack.EMPTY); // Purpur
|
|
this.clearContainer(player, this.enchantSlots);
|
|
});
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
|
|
index 89838076f3231ff4318ebb2718c9406399b4e4f5..d41987060c2261f1a345752ecc46af1ec23b83ea 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
|
|
@@ -95,9 +95,11 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
|
|
@Override
|
|
public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {
|
|
+ ItemStack itemstack = activeQuickItem == null ? stack : activeQuickItem; // Purpur
|
|
context.execute((world, blockposition) -> {
|
|
+ org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent grindstoneTakeResultEvent = new org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), this.getExperienceAmount(world)); grindstoneTakeResultEvent.callEvent(); // Purpur
|
|
if (world instanceof ServerLevel) {
|
|
- ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), this.getExperienceAmount(world), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Paper
|
|
+ ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), grindstoneTakeResultEvent.getExperienceAmount(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Paper // Purpur
|
|
}
|
|
|
|
world.levelEvent(1042, blockposition, 0);
|
|
@@ -130,7 +132,7 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
Enchantment enchantment = (Enchantment) entry.getKey();
|
|
Integer integer = (Integer) entry.getValue();
|
|
|
|
- if (!enchantment.isCurse()) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(enchantment)) { // Purpur
|
|
j += enchantment.getMinCost(integer);
|
|
}
|
|
}
|
|
@@ -230,7 +232,7 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
Entry<Enchantment, Integer> entry = (Entry) iterator.next();
|
|
Enchantment enchantment = (Enchantment) entry.getKey();
|
|
|
|
- if (!enchantment.isCurse() || EnchantmentHelper.getItemEnchantmentLevel(enchantment, itemstack2) == 0) {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(enchantment) || EnchantmentHelper.getItemEnchantmentLevel(enchantment, itemstack2) == 0) { // Purpur
|
|
itemstack2.enchant(enchantment, (Integer) entry.getValue());
|
|
}
|
|
}
|
|
@@ -251,7 +253,7 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
|
|
itemstack1.setCount(amount);
|
|
Map<Enchantment, Integer> map = (Map) EnchantmentHelper.getEnchantments(item).entrySet().stream().filter((entry) -> {
|
|
- return ((Enchantment) entry.getKey()).isCurse();
|
|
+ return org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains((Enchantment) entry.getKey()); // Purpur
|
|
}).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
|
|
|
EnchantmentHelper.setEnchantments(map, itemstack1);
|
|
@@ -267,6 +269,20 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
itemstack1.setRepairCost(AnvilMenu.calculateIncreasedRepairCost(itemstack1.getBaseRepairCost()));
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveAttributes && itemstack1.getTag() != null) {
|
|
+ for (String key : itemstack1.getTag().getAllKeys()) {
|
|
+ if (!key.equals("display")) {
|
|
+ itemstack1.getTag().remove(key);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveDisplay && itemstack1.getTag() != null) {
|
|
+ itemstack1.getTag().remove("display");
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return itemstack1;
|
|
}
|
|
|
|
@@ -328,7 +344,9 @@ public class GrindstoneMenu extends AbstractContainerMenu {
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
+ this.activeQuickItem = itemstack; // Purpur
|
|
slot1.onTake(player, itemstack1);
|
|
+ this.activeQuickItem = null; // Purpur
|
|
}
|
|
|
|
return itemstack;
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/InventoryMenu.java b/src/main/java/net/minecraft/world/inventory/InventoryMenu.java
|
|
index da0f5c5e6ca7ce7b38792e6da52c5cdcdbae3b78..4136bcd49fe05d916ab65de0e866145185db4204 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/InventoryMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/InventoryMenu.java
|
|
@@ -4,6 +4,7 @@ import com.mojang.datafixers.util.Pair;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.world.Container;
|
|
+import net.minecraft.world.effect.MobEffects;
|
|
import net.minecraft.world.entity.EquipmentSlot;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.player.Inventory;
|
|
@@ -95,7 +96,7 @@ public class InventoryMenu extends RecipeBookMenu<CraftingContainer> {
|
|
public boolean mayPickup(Player playerEntity) {
|
|
ItemStack itemstack = this.getItem();
|
|
|
|
- return !itemstack.isEmpty() && !playerEntity.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? false : super.mayPickup(playerEntity);
|
|
+ return !itemstack.isEmpty() && !playerEntity.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? playerEntity.level.purpurConfig.playerRemoveBindingWithWeakness && playerEntity.hasEffect(MobEffects.WEAKNESS) : super.mayPickup(playerEntity); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java
|
|
index ff770b9ce68a62418de0c7ed389650626fa1dcb2..102739c0089ff3f6b3432f954304d43a3dfebc35 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java
|
|
@@ -177,7 +177,9 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu {
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
+ this.activeQuickItem = itemstack; // Purpur
|
|
slot1.onTake(player, itemstack1);
|
|
+ this.activeQuickItem = null; // Purpur
|
|
}
|
|
|
|
return itemstack;
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
|
index 4703f23316f82a1a942907b46d2d6dcb7d70ec37..162798f57a05b78121fa6c4fadf5adee80fbe221 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
|
@@ -30,11 +30,18 @@ public class PlayerEnderChestContainer extends SimpleContainer {
|
|
}
|
|
|
|
public PlayerEnderChestContainer(Player owner) {
|
|
- super(27);
|
|
+ super(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? 54 : 27); // Purpur
|
|
this.owner = owner;
|
|
// CraftBukkit end
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getContainerSize() {
|
|
+ return owner.sixRowEnderchestSlotCount < 0 ? super.getContainerSize() : owner.sixRowEnderchestSlotCount;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void setActiveChest(EnderChestBlockEntity blockEntity) {
|
|
this.activeChest = blockEntity;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/ArmorItem.java b/src/main/java/net/minecraft/world/item/ArmorItem.java
|
|
index d7a0cbde8f8c99276307502674c71463fbe7e89c..3500c56cb85d8c76b2acd77976d374eaf487b3b3 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ArmorItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ArmorItem.java
|
|
@@ -60,7 +60,7 @@ public class ArmorItem extends Item implements Equipable {
|
|
return false;
|
|
} else {
|
|
LivingEntity entityliving = (LivingEntity) list.get(0);
|
|
- EquipmentSlot enumitemslot = Mob.getEquipmentSlotForItem(armor);
|
|
+ EquipmentSlot enumitemslot = pointer.getLevel().purpurConfig.dispenserApplyCursedArmor ? Mob.getEquipmentSlotForItem(armor) : Mob.getSlotForDispenser(armor); if (enumitemslot == null) return false; // Purpur
|
|
ItemStack itemstack1 = armor.copyWithCount(1); // Paper - shrink below and single item in event
|
|
// CraftBukkit start
|
|
Level world = pointer.getLevel();
|
|
diff --git a/src/main/java/net/minecraft/world/item/ArmorStandItem.java b/src/main/java/net/minecraft/world/item/ArmorStandItem.java
|
|
index 7cffc64573008502bdd14ae4906fe51166b12fb3..1feafdbb48cf760cb6ebf95d5be2c32bdb1ad44f 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ArmorStandItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ArmorStandItem.java
|
|
@@ -58,6 +58,14 @@ public class ArmorStandItem extends Item {
|
|
return InteractionResult.FAIL;
|
|
}
|
|
// CraftBukkit end
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.persistentDroppableEntityDisplayNames && itemstack.hasCustomHoverName()) {
|
|
+ entityarmorstand.setCustomName(itemstack.getHoverName());
|
|
+ if (world.purpurConfig.armorstandSetNameVisible) {
|
|
+ entityarmorstand.setCustomNameVisible(true);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
worldserver.addFreshEntityWithPassengers(entityarmorstand);
|
|
world.playSound((Player) null, entityarmorstand.getX(), entityarmorstand.getY(), entityarmorstand.getZ(), SoundEvents.ARMOR_STAND_PLACE, SoundSource.BLOCKS, 0.75F, 0.8F);
|
|
entityarmorstand.gameEvent(GameEvent.ENTITY_PLACE, context.getPlayer());
|
|
diff --git a/src/main/java/net/minecraft/world/item/AxeItem.java b/src/main/java/net/minecraft/world/item/AxeItem.java
|
|
index 9c7d0b9cc2fa98d5785c914c0183f7d4b5b1c1ea..89a4ab17ca8d2aa1f52b041c610d7de19bf55e66 100644
|
|
--- a/src/main/java/net/minecraft/world/item/AxeItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/AxeItem.java
|
|
@@ -33,29 +33,32 @@ public class AxeItem extends DiggerItem {
|
|
BlockPos blockPos = context.getClickedPos();
|
|
Player player = context.getPlayer();
|
|
BlockState blockState = level.getBlockState(blockPos);
|
|
- Optional<BlockState> optional = this.getStripped(blockState);
|
|
- Optional<BlockState> optional2 = WeatheringCopper.getPrevious(blockState);
|
|
- Optional<BlockState> optional3 = Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(blockState.getBlock())).map((block) -> {
|
|
- return block.withPropertiesOf(blockState);
|
|
- });
|
|
+ // Purpur start
|
|
+ Block clickedBlock = level.getBlockState(blockPos).getBlock();
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional = Optional.ofNullable(level.purpurConfig.axeStrippables.get(blockState.getBlock()));
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional2 = Optional.ofNullable(level.purpurConfig.axeWeatherables.get(blockState.getBlock()));
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional3 = Optional.ofNullable(level.purpurConfig.axeWaxables.get(blockState.getBlock()));
|
|
+ // Purpur end
|
|
ItemStack itemStack = context.getItemInHand();
|
|
- Optional<BlockState> optional4 = Optional.empty();
|
|
+ Optional<org.purpurmc.purpur.tool.Actionable> optional4 = Optional.empty(); // Purpur
|
|
if (optional.isPresent()) {
|
|
- level.playSound(player, blockPos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ if (!STRIPPABLES.containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
optional4 = optional;
|
|
} else if (optional2.isPresent()) {
|
|
- level.playSound(player, blockPos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ if (!HoneycombItem.WAXABLES.get().containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
level.levelEvent(player, 3005, blockPos, 0);
|
|
optional4 = optional2;
|
|
} else if (optional3.isPresent()) {
|
|
- level.playSound(player, blockPos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ if (!HoneycombItem.WAX_OFF_BY_BLOCK.get().containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
level.levelEvent(player, 3004, blockPos, 0);
|
|
optional4 = optional3;
|
|
}
|
|
|
|
if (optional4.isPresent()) {
|
|
+ org.purpurmc.purpur.tool.Actionable actionable = optional4.get();
|
|
+ BlockState state = actionable.into().withPropertiesOf(blockState); // Purpur
|
|
// Paper start - EntityChangeBlockEvent
|
|
- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, optional4.get()).isCancelled()) {
|
|
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, state).isCancelled()) { // Purpur
|
|
return InteractionResult.PASS;
|
|
}
|
|
// Paper end
|
|
@@ -63,15 +66,22 @@ public class AxeItem extends DiggerItem {
|
|
CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, blockPos, itemStack);
|
|
}
|
|
|
|
- level.setBlock(blockPos, optional4.get(), 11);
|
|
- level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(player, optional4.get()));
|
|
+ // Purpur start
|
|
+ level.setBlock(blockPos, state, 11);
|
|
+ actionable.drops().forEach((drop, chance) -> {
|
|
+ if (level.random.nextDouble() < chance) {
|
|
+ Block.popResourceFromFace(level, blockPos, context.getClickedFace(), new ItemStack(drop));
|
|
+ }
|
|
+ });
|
|
+ level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(player, state));
|
|
+ // Purpur end
|
|
if (player != null) {
|
|
itemStack.hurtAndBreak(1, player, (p) -> {
|
|
p.broadcastBreakEvent(context.getHand());
|
|
});
|
|
}
|
|
|
|
- return InteractionResult.sidedSuccess(level.isClientSide);
|
|
+ return InteractionResult.SUCCESS; // Purpur - force arm swing
|
|
} else {
|
|
return InteractionResult.PASS;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java
|
|
index ebee8de2ed831755b6fd154f6cc77ac993839bb9..9060a844cd3bb3b62171872d84516b9195b9b677 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BlockItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BlockItem.java
|
|
@@ -153,7 +153,24 @@ public class BlockItem extends Item {
|
|
}
|
|
|
|
protected boolean updateCustomBlockEntityTag(BlockPos pos, Level world, @Nullable Player player, ItemStack stack, BlockState state) {
|
|
- return BlockItem.updateCustomBlockEntityTag(world, player, pos, stack);
|
|
+ // Purpur start
|
|
+ boolean handled = updateCustomBlockEntityTag(world, player, pos, stack);
|
|
+ if (world.purpurConfig.persistentTileEntityDisplayNames && stack.hasTag()) {
|
|
+ CompoundTag display = stack.getTagElement("display");
|
|
+ if (display != null) {
|
|
+ BlockEntity blockEntity = world.getBlockEntity(pos);
|
|
+ if (blockEntity != null) {
|
|
+ if (display.contains("Name", 8)) {
|
|
+ blockEntity.setPersistentDisplayName(display.getString("Name"));
|
|
+ }
|
|
+ if (display.contains("Lore", 9)) {
|
|
+ blockEntity.setPersistentLore(display.getList("Lore", 8));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return handled;
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Nullable
|
|
@@ -288,7 +305,7 @@ public class BlockItem extends Item {
|
|
|
|
@Override
|
|
public void onDestroyed(ItemEntity entity) {
|
|
- if (this.block instanceof ShulkerBoxBlock) {
|
|
+ if (this.block instanceof ShulkerBoxBlock && entity.level.purpurConfig.shulkerBoxItemDropContentsWhenDestroyed) {
|
|
ItemStack itemstack = entity.getItem();
|
|
CompoundTag nbttagcompound = BlockItem.getBlockEntityData(itemstack);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/item/BoatItem.java b/src/main/java/net/minecraft/world/item/BoatItem.java
|
|
index 1a95ac11a2fbc811c89afa3adf38e0fc9eaab09b..91280f8c39ea191b90da2a9ff5c49f43c255bd9a 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BoatItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BoatItem.java
|
|
@@ -69,6 +69,11 @@ public class BoatItem extends Item {
|
|
|
|
entityboat.setVariant(this.type);
|
|
entityboat.setYRot(user.getYRot());
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.persistentDroppableEntityDisplayNames && itemstack.hasCustomHoverName()) {
|
|
+ entityboat.setCustomName(itemstack.getHoverName());
|
|
+ }
|
|
+ // Purpur end
|
|
if (!world.noCollision(entityboat, entityboat.getBoundingBox())) {
|
|
return InteractionResultHolder.fail(itemstack);
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/item/BowItem.java b/src/main/java/net/minecraft/world/item/BowItem.java
|
|
index 08d597db1a5345a343777a01427655e6bf2c926b..d45a2f49c82d00801578c34e5f5277fc5e82be87 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BowItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BowItem.java
|
|
@@ -38,13 +38,13 @@ public class BowItem extends ProjectileWeaponItem implements Vanishable {
|
|
float f = BowItem.getPowerForTime(j);
|
|
|
|
if ((double) f >= 0.1D) {
|
|
- boolean flag1 = flag && itemstack1.is(Items.ARROW);
|
|
+ boolean flag1 = flag && ((itemstack1.is(Items.ARROW) && world.purpurConfig.infinityWorksWithNormalArrows) || (itemstack1.is(Items.TIPPED_ARROW) && world.purpurConfig.infinityWorksWithTippedArrows) || (itemstack1.is(Items.SPECTRAL_ARROW) && world.purpurConfig.infinityWorksWithSpectralArrows)); // Purpur if (!world.isClientSide) {
|
|
|
|
if (!world.isClientSide) {
|
|
ArrowItem itemarrow = (ArrowItem) (itemstack1.getItem() instanceof ArrowItem ? itemstack1.getItem() : Items.ARROW);
|
|
AbstractArrow entityarrow = itemarrow.createArrow(world, itemstack1, entityhuman);
|
|
|
|
- entityarrow.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, f * 3.0F, 1.0F);
|
|
+ entityarrow.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, f * 3.0F, (float) world.purpurConfig.bowProjectileOffset); // Purpur
|
|
if (f == 1.0F) {
|
|
entityarrow.setCritArrow(true);
|
|
}
|
|
@@ -64,6 +64,13 @@ public class BowItem extends ProjectileWeaponItem implements Vanishable {
|
|
if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.FLAMING_ARROWS, stack) > 0) {
|
|
entityarrow.setSecondsOnFire(100);
|
|
}
|
|
+ // Purpur start
|
|
+ int lootingLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.MOB_LOOTING, stack);
|
|
+
|
|
+ if (lootingLevel > 0) {
|
|
+ entityarrow.setLootingLevel(lootingLevel);
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start
|
|
org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(entityhuman, stack, itemstack1, entityarrow, entityhuman.getUsedItemHand(), f, !flag1);
|
|
if (event.isCancelled()) {
|
|
@@ -132,7 +139,7 @@ public class BowItem extends ProjectileWeaponItem implements Vanishable {
|
|
ItemStack itemstack = user.getItemInHand(hand);
|
|
boolean flag = !user.getProjectile(itemstack).isEmpty();
|
|
|
|
- if (!user.getAbilities().instabuild && !flag) {
|
|
+ if (!(world.purpurConfig.infinityWorksWithoutArrows && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY_ARROWS, itemstack) > 0) && !user.getAbilities().instabuild && !flag) { // Purpur
|
|
return InteractionResultHolder.fail(itemstack);
|
|
} else {
|
|
user.startUsingItem(hand);
|
|
diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java
|
|
index 5c6aa9c464784ad5ee366412d080c72d3d22a76f..c03abc9589bf5f37abc1b0d355ed9784bac31a93 100644
|
|
--- a/src/main/java/net/minecraft/world/item/BucketItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/BucketItem.java
|
|
@@ -166,7 +166,7 @@ public class BucketItem extends Item implements DispensibleContainerItem {
|
|
// CraftBukkit end
|
|
if (!flag1) {
|
|
return movingobjectpositionblock != null && this.emptyContents(entityhuman, world, movingobjectpositionblock.getBlockPos().relative(movingobjectpositionblock.getDirection()), (BlockHitResult) null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit
|
|
- } else if (world.dimensionType().ultraWarm() && this.content.is(FluidTags.WATER)) {
|
|
+ } else if ((world.dimensionType().ultraWarm() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) && this.content.is(FluidTags.WATER)) { // Purpur
|
|
int i = blockposition.getX();
|
|
int j = blockposition.getY();
|
|
int k = blockposition.getZ();
|
|
@@ -174,7 +174,7 @@ public class BucketItem extends Item implements DispensibleContainerItem {
|
|
world.playSound(entityhuman, blockposition, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F);
|
|
|
|
for (int l = 0; l < 8; ++l) {
|
|
- world.addParticle(ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D);
|
|
+ ((ServerLevel) world).sendParticles(null, ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 1, 0.0D, 0.0D, 0.0D, 0.0D, true); // Purpur
|
|
}
|
|
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java
|
|
index bc4f04c2512191da3c9e1c49f0716bb9128fc754..310e03d8cc07f95927d9806fc80a4215283d2ef5 100644
|
|
--- a/src/main/java/net/minecraft/world/item/CrossbowItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java
|
|
@@ -64,7 +64,7 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable {
|
|
ItemStack itemstack = user.getItemInHand(hand);
|
|
|
|
if (CrossbowItem.isCharged(itemstack)) {
|
|
- CrossbowItem.performShooting(world, user, hand, itemstack, CrossbowItem.getShootingPower(itemstack), 1.0F);
|
|
+ CrossbowItem.performShooting(world, user, hand, itemstack, CrossbowItem.getShootingPower(itemstack), (float) world.purpurConfig.crossbowProjectileOffset); // Purpur
|
|
CrossbowItem.setCharged(itemstack, false);
|
|
return InteractionResultHolder.consume(itemstack);
|
|
} else if (!user.getProjectile(itemstack).isEmpty()) {
|
|
@@ -113,7 +113,7 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable {
|
|
// Paper end
|
|
int i = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.MULTISHOT, projectile);
|
|
int j = i == 0 ? 1 : 3;
|
|
- boolean flag = !consume || shooter instanceof Player && ((Player) shooter).getAbilities().instabuild; // Paper - add consume
|
|
+ boolean flag = !consume || shooter instanceof Player && ((Player) shooter).getAbilities().instabuild || (org.purpurmc.purpur.PurpurConfig.allowCrossbowInfinity && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY_ARROWS, projectile) > 0); // Paper - add consume // Purpur
|
|
ItemStack itemstack1 = shooter.getProjectile(projectile);
|
|
ItemStack itemstack2 = itemstack1.copy();
|
|
|
|
@@ -294,6 +294,14 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable {
|
|
entityarrow.setPierceLevel((byte) i);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ int lootingLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.MOB_LOOTING, crossbow);
|
|
+
|
|
+ if (lootingLevel > 0) {
|
|
+ entityarrow.setLootingLevel(lootingLevel);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return entityarrow;
|
|
}
|
|
|
|
@@ -303,7 +311,7 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable {
|
|
|
|
for (int i = 0; i < list.size(); ++i) {
|
|
ItemStack itemstack1 = (ItemStack) list.get(i);
|
|
- boolean flag = entity instanceof Player && ((Player) entity).getAbilities().instabuild;
|
|
+ boolean flag = entity instanceof Player && ((Player) entity).getAbilities().instabuild || (org.purpurmc.purpur.PurpurConfig.allowCrossbowInfinity && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY_ARROWS, stack) > 0); // Purpur
|
|
|
|
if (!itemstack1.isEmpty()) {
|
|
if (i == 0) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/DyeColor.java b/src/main/java/net/minecraft/world/item/DyeColor.java
|
|
index 2170715ed0e81a3055e4ab546c8b294c5ef7f142..beae4e2b9f61df83215de860d64c4ce2d3482004 100644
|
|
--- a/src/main/java/net/minecraft/world/item/DyeColor.java
|
|
+++ b/src/main/java/net/minecraft/world/item/DyeColor.java
|
|
@@ -103,4 +103,10 @@ public enum DyeColor implements StringRepresentable {
|
|
public String getSerializedName() {
|
|
return this.name;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public static DyeColor random(net.minecraft.util.RandomSource random) {
|
|
+ return values()[random.nextInt(values().length)];
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/EggItem.java b/src/main/java/net/minecraft/world/item/EggItem.java
|
|
index 58cb992c5defec2f092755cbde661ff10f38bf9d..52f48681407d23f0925f4c9c072d5f0a2a6b1778 100644
|
|
--- a/src/main/java/net/minecraft/world/item/EggItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/EggItem.java
|
|
@@ -24,7 +24,7 @@ public class EggItem extends Item {
|
|
ThrownEgg entityegg = new ThrownEgg(world, user);
|
|
|
|
entityegg.setItem(itemstack);
|
|
- entityegg.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, 1.0F);
|
|
+ entityegg.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, (float) world.purpurConfig.eggProjectileOffset); // Purpur
|
|
// Paper start
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entityegg.getBukkitEntity());
|
|
if (event.callEvent() && world.addFreshEntity(entityegg)) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/EnderpearlItem.java b/src/main/java/net/minecraft/world/item/EnderpearlItem.java
|
|
index 749ab72edc0d2e9c6f1161415ab8d59d3d6ca976..6b27d98d06b163243bb0e1bb979aad03f48d7770 100644
|
|
--- a/src/main/java/net/minecraft/world/item/EnderpearlItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/EnderpearlItem.java
|
|
@@ -24,7 +24,7 @@ public class EnderpearlItem extends Item {
|
|
ThrownEnderpearl entityenderpearl = new ThrownEnderpearl(world, user);
|
|
|
|
entityenderpearl.setItem(itemstack);
|
|
- entityenderpearl.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, 1.0F);
|
|
+ entityenderpearl.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, (float) world.purpurConfig.enderPearlProjectileOffset); // Purpur
|
|
// Paper start
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entityenderpearl.getBukkitEntity());
|
|
if (event.callEvent() && world.addFreshEntity(entityenderpearl)) {
|
|
@@ -36,7 +36,7 @@ public class EnderpearlItem extends Item {
|
|
|
|
world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENDER_PEARL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (net.minecraft.world.entity.Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F));
|
|
user.awardStat(Stats.ITEM_USED.get(this));
|
|
- user.getCooldowns().addCooldown(this, 20);
|
|
+ user.getCooldowns().addCooldown(this, user.getAbilities().instabuild ? world.purpurConfig.enderPearlCooldownCreative : world.purpurConfig.enderPearlCooldown); // Purpur
|
|
} else {
|
|
// Paper end
|
|
if (user instanceof net.minecraft.server.level.ServerPlayer) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
|
|
index 82b0bda3e35ec2157a477e1a17b2b46baadc97d9..0fc45b1048a1c4e0dc2bd1ae0437eecbe113cf96 100644
|
|
--- a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
|
|
@@ -15,6 +15,7 @@ import net.minecraft.util.ByIdMap;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.InteractionResultHolder;
|
|
+import net.minecraft.world.entity.EquipmentSlot;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
|
import net.minecraft.world.item.context.UseOnContext;
|
|
@@ -69,6 +70,14 @@ public class FireworkRocketItem extends Item {
|
|
com.destroystokyo.paper.event.player.PlayerElytraBoostEvent event = new com.destroystokyo.paper.event.player.PlayerElytraBoostEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Firework) fireworkRocketEntity.getBukkitEntity());
|
|
if (event.callEvent() && world.addFreshEntity(fireworkRocketEntity)) {
|
|
user.awardStat(Stats.ITEM_USED.get(this));
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.elytraDamagePerFireworkBoost > 0) {
|
|
+ ItemStack chestItem = user.getItemBySlot(EquipmentSlot.CHEST);
|
|
+ if (chestItem.getItem() == Items.ELYTRA) {
|
|
+ chestItem.hurtAndBreak(world.purpurConfig.elytraDamagePerFireworkBoost, user, (entityliving) -> entityliving.broadcastBreakEvent(EquipmentSlot.CHEST));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
if (event.shouldConsume() && !user.getAbilities().instabuild) {
|
|
itemStack.shrink(1);
|
|
} else ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
|
|
diff --git a/src/main/java/net/minecraft/world/item/HangingEntityItem.java b/src/main/java/net/minecraft/world/item/HangingEntityItem.java
|
|
index b2ad6d230de2c29f371178bccde1111c7532ee70..6667926519a0f1c151e53f59cce36e7417dfc1cd 100644
|
|
--- a/src/main/java/net/minecraft/world/item/HangingEntityItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/HangingEntityItem.java
|
|
@@ -48,7 +48,7 @@ public class HangingEntityItem extends Item {
|
|
return InteractionResult.FAIL;
|
|
} else {
|
|
Level world = context.getLevel();
|
|
- Object object;
|
|
+ Entity object; // Purpur
|
|
|
|
if (this.type == EntityType.PAINTING) {
|
|
Optional<Painting> optional = Painting.create(world, blockposition1, enumdirection);
|
|
@@ -72,6 +72,11 @@ public class HangingEntityItem extends Item {
|
|
|
|
if (nbttagcompound != null) {
|
|
EntityType.updateCustomEntityTag(world, entityhuman, (Entity) object, nbttagcompound);
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.persistentDroppableEntityDisplayNames && itemstack.hasCustomHoverName()) {
|
|
+ object.setCustomName(itemstack.getHoverName());
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
if (((HangingEntity) object).survives()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/HoeItem.java b/src/main/java/net/minecraft/world/item/HoeItem.java
|
|
index 180aec596110309aade13d2080f8824d152b07cb..c4aec1e5135a79837918b692e75a7b55d5cffeb0 100644
|
|
--- a/src/main/java/net/minecraft/world/item/HoeItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/HoeItem.java
|
|
@@ -34,15 +34,23 @@ public class HoeItem extends DiggerItem {
|
|
public InteractionResult useOn(UseOnContext context) {
|
|
Level level = context.getLevel();
|
|
BlockPos blockPos = context.getClickedPos();
|
|
- Pair<Predicate<UseOnContext>, Consumer<UseOnContext>> pair = TILLABLES.get(level.getBlockState(blockPos).getBlock());
|
|
- if (pair == null) {
|
|
- return InteractionResult.PASS;
|
|
- } else {
|
|
- Predicate<UseOnContext> predicate = pair.getFirst();
|
|
- Consumer<UseOnContext> consumer = pair.getSecond();
|
|
+ // Purpur start
|
|
+ Block clickedBlock = level.getBlockState(blockPos).getBlock();
|
|
+ var tillable = level.purpurConfig.hoeTillables.get(level.getBlockState(blockPos).getBlock());
|
|
+ if (tillable == null) { return InteractionResult.PASS; } else {
|
|
+ Predicate<UseOnContext> predicate = tillable.condition().predicate();
|
|
+ Consumer<UseOnContext> consumer = (ctx) -> {
|
|
+ level.setBlock(blockPos, tillable.into().defaultBlockState(), 11);
|
|
+ tillable.drops().forEach((drop, chance) -> {
|
|
+ if (level.random.nextDouble() < chance) {
|
|
+ Block.popResourceFromFace(level, blockPos, ctx.getClickedFace(), new ItemStack(drop));
|
|
+ }
|
|
+ });
|
|
+ };
|
|
+ // Purpur end
|
|
if (predicate.test(context)) {
|
|
Player player = context.getPlayer();
|
|
- level.playSound(player, blockPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ if (!TILLABLES.containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound
|
|
if (!level.isClientSide) {
|
|
consumer.accept(context);
|
|
if (player != null) {
|
|
@@ -52,7 +60,7 @@ public class HoeItem extends DiggerItem {
|
|
}
|
|
}
|
|
|
|
- return InteractionResult.sidedSuccess(level.isClientSide);
|
|
+ return InteractionResult.SUCCESS; // Purpur - force arm swing
|
|
} else {
|
|
return InteractionResult.PASS;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
index d81fcbadc5c0b3d4b54dde5d47a0f847d8ec6918..aac8c78cec1c935d24207093a36b70a1ed082703 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
@@ -111,6 +111,7 @@ import org.bukkit.event.world.StructureGrowEvent;
|
|
|
|
public final class ItemStack {
|
|
|
|
+ public boolean isExactRecipeIngredient = false; // PaperPR
|
|
public static final Codec<ItemStack> CODEC = RecordCodecBuilder.create((instance) -> {
|
|
return instance.group(BuiltInRegistries.ITEM.byNameCodec().fieldOf("id").forGetter((itemstack) -> {
|
|
return itemstack.item;
|
|
@@ -417,6 +418,7 @@ public final class ItemStack {
|
|
world.preventPoiUpdated = true; // CraftBukkit - SPIGOT-5710
|
|
for (BlockState blockstate : blocks) {
|
|
blockstate.update(true, false);
|
|
+ ((CraftBlock) blockstate.getBlock()).getNMS().getBlock().forgetPlacer(); // Purpur
|
|
}
|
|
world.preventPoiUpdated = false;
|
|
|
|
@@ -446,6 +448,7 @@ public final class ItemStack {
|
|
if (!(block.getBlock() instanceof BaseEntityBlock)) { // Containers get placed automatically
|
|
block.getBlock().onPlace(block, world, newblockposition, oldBlock, true, itemactioncontext); // Paper - pass itemactioncontext
|
|
}
|
|
+ block.getBlock().forgetPlacer(); // Purpur
|
|
|
|
world.notifyAndUpdatePhysics(newblockposition, null, oldBlock, block, world.getBlockState(newblockposition), updateFlag, 512); // send null chunk as chunk.k() returns false by this point
|
|
}
|
|
@@ -566,6 +569,16 @@ public final class ItemStack {
|
|
return this.isDamageableItem() && this.getDamageValue() > 0;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public float getDamagePercent() {
|
|
+ if (isDamaged()) {
|
|
+ return (float) getDamageValue() / (float) getItem().getMaxDamage();
|
|
+ } else {
|
|
+ return 0F;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public int getDamageValue() {
|
|
return this.tag == null ? 0 : this.tag.getInt("Damage");
|
|
}
|
|
@@ -585,7 +598,7 @@ public final class ItemStack {
|
|
int j;
|
|
|
|
if (amount > 0) {
|
|
- j = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.UNBREAKING, this);
|
|
+ j = (getItem() == Items.ELYTRA && player != null && player.level.purpurConfig.elytraIgnoreUnbreaking) ? 0 : EnchantmentHelper.getItemEnchantmentLevel(Enchantments.UNBREAKING, this);
|
|
int k = 0;
|
|
|
|
for (int l = 0; j > 0 && l < amount; ++l) {
|
|
@@ -640,6 +653,12 @@ public final class ItemStack {
|
|
if (this.hurt(amount, entity.getRandom(), entity /*instanceof ServerPlayer ? (ServerPlayer) entity : null*/)) { // Paper - pass LivingEntity for EntityItemDamageEvent
|
|
breakCallback.accept(entity);
|
|
Item item = this.getItem();
|
|
+ // Purpur start
|
|
+ if (item == Items.ELYTRA) {
|
|
+ setDamageValue(item.getMaxDamage() - 1);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start - Check for item breaking
|
|
if (this.count == 1 && entity instanceof net.minecraft.world.entity.player.Player) {
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((net.minecraft.world.entity.player.Player) entity, this);
|
|
@@ -1172,7 +1191,7 @@ public final class ItemStack {
|
|
|
|
ListTag nbttaglist = this.tag.getList("Enchantments", 10);
|
|
|
|
- nbttaglist.add(EnchantmentHelper.storeEnchantment(EnchantmentHelper.getEnchantmentId(enchantment), (byte) level));
|
|
+ nbttaglist.add(EnchantmentHelper.storeEnchantment(EnchantmentHelper.getEnchantmentId(enchantment), (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels) ? (byte) level : (short) level)); // Purpur
|
|
processEnchantOrder(this.tag); // Paper
|
|
}
|
|
|
|
@@ -1180,6 +1199,12 @@ public final class ItemStack {
|
|
return this.tag != null && this.tag.contains("Enchantments", 9) ? !this.tag.getList("Enchantments", 10).isEmpty() : false;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public boolean hasEnchantment(Enchantment enchantment) {
|
|
+ return isEnchanted() && EnchantmentHelper.deserializeEnchantments(getEnchantmentTags()).containsKey(enchantment);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public void addTagElement(String key, Tag element) {
|
|
this.getOrCreateTag().put(key, element);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/Items.java b/src/main/java/net/minecraft/world/item/Items.java
|
|
index 775823daa5187804d27e5ee696cd75f703bb067c..bc0915913d3d8fbe145ee7e19133c7de922e0c80 100644
|
|
--- a/src/main/java/net/minecraft/world/item/Items.java
|
|
+++ b/src/main/java/net/minecraft/world/item/Items.java
|
|
@@ -292,7 +292,7 @@ public class Items {
|
|
public static final Item PURPUR_BLOCK = registerBlock(Blocks.PURPUR_BLOCK);
|
|
public static final Item PURPUR_PILLAR = registerBlock(Blocks.PURPUR_PILLAR);
|
|
public static final Item PURPUR_STAIRS = registerBlock(Blocks.PURPUR_STAIRS);
|
|
- public static final Item SPAWNER = registerBlock(Blocks.SPAWNER);
|
|
+ public static final Item SPAWNER = registerBlock(new org.purpurmc.purpur.item.SpawnerItem(Blocks.SPAWNER, new Item.Properties().rarity(Rarity.EPIC))); // Purpur
|
|
public static final Item CHEST = registerBlock(Blocks.CHEST);
|
|
public static final Item CRAFTING_TABLE = registerBlock(Blocks.CRAFTING_TABLE);
|
|
public static final Item FARMLAND = registerBlock(Blocks.FARMLAND);
|
|
@@ -1178,7 +1178,7 @@ public class Items {
|
|
public static final Item LANTERN = registerBlock(Blocks.LANTERN);
|
|
public static final Item SOUL_LANTERN = registerBlock(Blocks.SOUL_LANTERN);
|
|
public static final Item SWEET_BERRIES = registerItem("sweet_berries", new ItemNameBlockItem(Blocks.SWEET_BERRY_BUSH, (new Item.Properties()).food(Foods.SWEET_BERRIES)));
|
|
- public static final Item GLOW_BERRIES = registerItem("glow_berries", new ItemNameBlockItem(Blocks.CAVE_VINES, (new Item.Properties()).food(Foods.GLOW_BERRIES)));
|
|
+ public static final Item GLOW_BERRIES = registerItem("glow_berries", new org.purpurmc.purpur.item.GlowBerryItem(Blocks.CAVE_VINES, (new Item.Properties()).food(Foods.GLOW_BERRIES))); // Purpur
|
|
public static final Item CAMPFIRE = registerBlock(Blocks.CAMPFIRE);
|
|
public static final Item SOUL_CAMPFIRE = registerBlock(Blocks.SOUL_CAMPFIRE);
|
|
public static final Item SHROOMLIGHT = registerBlock(Blocks.SHROOMLIGHT);
|
|
@@ -1278,6 +1278,13 @@ public class Items {
|
|
((BlockItem)item).registerBlocks(Item.BY_BLOCK, item);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (item.getFoodProperties() != null) {
|
|
+ Foods.ALL_PROPERTIES.put(id.getPath(), item.getFoodProperties());
|
|
+ Foods.DEFAULT_PROPERTIES.put(id.getPath(), item.getFoodProperties().copy());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return Registry.register(BuiltInRegistries.ITEM, id, item);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/MilkBucketItem.java b/src/main/java/net/minecraft/world/item/MilkBucketItem.java
|
|
index f33977d95b6db473be4f95075ba99caf90ad0220..56dc04d8875971ee9a5d077a695509af74fe2473 100644
|
|
--- a/src/main/java/net/minecraft/world/item/MilkBucketItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/MilkBucketItem.java
|
|
@@ -5,6 +5,8 @@ import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.stats.Stats;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.InteractionResultHolder;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import net.minecraft.world.effect.MobEffects;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.level.Level;
|
|
@@ -31,7 +33,9 @@ public class MilkBucketItem extends Item {
|
|
}
|
|
|
|
if (!world.isClientSide) {
|
|
+ MobEffectInstance badOmen = user.getEffect(MobEffects.BAD_OMEN);
|
|
user.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.MILK); // CraftBukkit
|
|
+ if (!world.purpurConfig.milkCuresBadOmen && badOmen != null) user.addEffect(badOmen); // Purpur
|
|
}
|
|
|
|
return stack.isEmpty() ? new ItemStack(Items.BUCKET) : stack;
|
|
diff --git a/src/main/java/net/minecraft/world/item/MinecartItem.java b/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
index c6d2f764efa9b8bec730bbe757d480e365b25ccc..33a30d26da2401535f0a72acb2bbffec1aef151e 100644
|
|
--- a/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
@@ -120,8 +120,9 @@ public class MinecartItem extends Item {
|
|
BlockState iblockdata = world.getBlockState(blockposition);
|
|
|
|
if (!iblockdata.is(BlockTags.RAILS)) {
|
|
- return InteractionResult.FAIL;
|
|
- } else {
|
|
+ if (!world.purpurConfig.minecartPlaceAnywhere) return InteractionResult.FAIL;
|
|
+ if (iblockdata.getMaterial().isSolid()) blockposition = blockposition.relative(context.getClickedFace());
|
|
+ } // else { // Purpur - place minecarts anywhere
|
|
ItemStack itemstack = context.getItemInHand();
|
|
|
|
if (!world.isClientSide) {
|
|
@@ -149,6 +150,6 @@ public class MinecartItem extends Item {
|
|
|
|
itemstack.shrink(1);
|
|
return InteractionResult.sidedSuccess(world.isClientSide);
|
|
- }
|
|
+ // } // Purpur - place minecarts anywhere
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/NameTagItem.java b/src/main/java/net/minecraft/world/item/NameTagItem.java
|
|
index 623f78c078fb3aa2665d7e8a37672438227bce6b..500c69e555c7247e20ef8cc59d83415578f44427 100644
|
|
--- a/src/main/java/net/minecraft/world/item/NameTagItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/NameTagItem.java
|
|
@@ -24,6 +24,7 @@ public class NameTagItem extends Item {
|
|
if (!event.callEvent()) return InteractionResult.PASS;
|
|
LivingEntity newEntityLiving = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getEntity()).getHandle();
|
|
newEntityLiving.setCustomName(event.getName() != null ? PaperAdventure.asVanilla(event.getName()) : null);
|
|
+ if (user.level.purpurConfig.armorstandFixNametags && entity instanceof net.minecraft.world.entity.decoration.ArmorStand) entity.setCustomNameVisible(true); // Purpur
|
|
if (event.isPersistent() && newEntityLiving instanceof Mob) {
|
|
((Mob) newEntityLiving).setPersistenceRequired();
|
|
// Paper end
|
|
diff --git a/src/main/java/net/minecraft/world/item/ShovelItem.java b/src/main/java/net/minecraft/world/item/ShovelItem.java
|
|
index c7195f2e12bbd6545f7bffcc2b4ba5cc3d48df20..5e730bc9c8ff94b16ac2bf8567dda8aea2ee4b2a 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ShovelItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ShovelItem.java
|
|
@@ -34,7 +34,7 @@ public class ShovelItem extends DiggerItem {
|
|
return InteractionResult.PASS;
|
|
} else {
|
|
Player player = context.getPlayer();
|
|
- BlockState blockState2 = FLATTENABLES.get(blockState.getBlock());
|
|
+ BlockState blockState2 = level.purpurConfig.shovelTurnsBlockToGrassPath.contains(blockState.getBlock()) ? Blocks.DIRT_PATH.defaultBlockState() : null; // Purpur
|
|
BlockState blockState3 = null;
|
|
Runnable afterAction = null; // Paper
|
|
if (blockState2 != null && level.getBlockState(blockPos.above()).isAir()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/SnowballItem.java b/src/main/java/net/minecraft/world/item/SnowballItem.java
|
|
index ef3f90a5bcdd7b9815a4053cff166f9d2552f55d..e7e5e1cc92f56e3daba8fa09c59188febec5e8f2 100644
|
|
--- a/src/main/java/net/minecraft/world/item/SnowballItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/SnowballItem.java
|
|
@@ -25,7 +25,7 @@ public class SnowballItem extends Item {
|
|
Snowball entitysnowball = new Snowball(world, user);
|
|
|
|
entitysnowball.setItem(itemstack);
|
|
- entitysnowball.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, 1.0F);
|
|
+ entitysnowball.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, (float) world.purpurConfig.snowballProjectileOffset); // Purpur
|
|
// Paper start
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entitysnowball.getBukkitEntity());
|
|
if (event.callEvent() && world.addFreshEntity(entitysnowball)) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/SpawnEggItem.java b/src/main/java/net/minecraft/world/item/SpawnEggItem.java
|
|
index 31268e25056f980798ef7db72c4f955a074cc639..955822da142a2463af536dd1ce48037deda41402 100644
|
|
--- a/src/main/java/net/minecraft/world/item/SpawnEggItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/SpawnEggItem.java
|
|
@@ -68,6 +68,15 @@ public class SpawnEggItem extends Item {
|
|
SpawnerBlockEntity tileentitymobspawner = (SpawnerBlockEntity) tileentity;
|
|
EntityType<?> entitytypes = this.getType(itemstack.getTag());
|
|
|
|
+ // Purpur start
|
|
+ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
|
+ org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.CreatureSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(entitytypes.getName()));
|
|
+ if (!event.callEvent()) {
|
|
+ return InteractionResult.FAIL;
|
|
+ }
|
|
+ entitytypes = EntityType.getFromBukkitType(event.getEntityType());
|
|
+ // Purpur end
|
|
+
|
|
tileentitymobspawner.setEntityId(entitytypes, world.getRandom());
|
|
tileentity.setChanged();
|
|
world.sendBlockUpdated(blockposition, iblockdata, iblockdata, 3);
|
|
diff --git a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
|
|
index de5bdceb4c8578fb972a2fd5ee0dfdae509e46dc..bcf63ccb6e679cb97d658780b2663aafa3568bcb 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
|
|
@@ -18,7 +18,7 @@ public class ThrowablePotionItem extends PotionItem {
|
|
if (!world.isClientSide) {
|
|
ThrownPotion thrownPotion = new ThrownPotion(world, user);
|
|
thrownPotion.setItem(itemStack);
|
|
- thrownPotion.shootFromRotation(user, user.getXRot(), user.getYRot(), -20.0F, 0.5F, 1.0F);
|
|
+ thrownPotion.shootFromRotation(user, user.getXRot(), user.getYRot(), -20.0F, 0.5F, (float) world.purpurConfig.throwablePotionProjectileOffset); // Purpur
|
|
// Paper start
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownPotion.getBukkitEntity());
|
|
if (event.callEvent() && world.addFreshEntity(thrownPotion)) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java
|
|
index 9365f886a23a71c41091b22d46896ff18a5a0635..d35432087c70ce66b74d1e27df19f462f22b1aa1 100644
|
|
--- a/src/main/java/net/minecraft/world/item/TridentItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/TridentItem.java
|
|
@@ -77,11 +77,19 @@ public class TridentItem extends Item implements Vanishable {
|
|
if (k == 0) {
|
|
ThrownTrident entitythrowntrident = new ThrownTrident(world, entityhuman, stack);
|
|
|
|
- entitythrowntrident.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, 2.5F + (float) k * 0.5F, 1.0F);
|
|
+ entitythrowntrident.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, 2.5F + (float) k * 0.5F, (float) world.purpurConfig.tridentProjectileOffset); // Purpur
|
|
if (entityhuman.getAbilities().instabuild) {
|
|
entitythrowntrident.pickup = AbstractArrow.Pickup.CREATIVE_ONLY;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ int lootingLevel = EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MOB_LOOTING, stack);
|
|
+
|
|
+ if (lootingLevel > 0) {
|
|
+ entitythrowntrident.setLootingLevel(lootingLevel);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// CraftBukkit start
|
|
// Paper start
|
|
com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) entitythrowntrident.getBukkitEntity());
|
|
@@ -130,6 +138,14 @@ public class TridentItem extends Item implements Vanishable {
|
|
f2 *= f6 / f5;
|
|
f3 *= f6 / f5;
|
|
f4 *= f6 / f5;
|
|
+
|
|
+ // Purpur start
|
|
+ ItemStack chestItem = entityhuman.getItemBySlot(EquipmentSlot.CHEST);
|
|
+ if (chestItem.getItem() == Items.ELYTRA && world.purpurConfig.elytraDamagePerTridentBoost > 0) {
|
|
+ chestItem.hurtAndBreak(world.purpurConfig.elytraDamagePerTridentBoost, entityhuman, (entity) -> entity.broadcastBreakEvent(EquipmentSlot.CHEST));
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
entityhuman.push((double) f2, (double) f3, (double) f4);
|
|
entityhuman.startAutoSpinAttack(20);
|
|
if (entityhuman.isOnGround()) {
|
|
diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
|
|
index 8d4aca59bd7518179520f4d4fb7137778e232d90..e24034d1ce4bb529de084aab69a531227e0c2f79 100644
|
|
--- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
|
|
+++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
|
|
@@ -39,6 +39,7 @@ public final class Ingredient implements Predicate<ItemStack> {
|
|
@Nullable
|
|
private IntList stackingIds;
|
|
public boolean exact; // CraftBukkit
|
|
+ public Predicate<org.bukkit.inventory.ItemStack> predicate;
|
|
|
|
public Ingredient(Stream<? extends Ingredient.Value> entries) {
|
|
this.values = (Ingredient.Value[]) entries.toArray((i) -> {
|
|
@@ -50,7 +51,11 @@ public final class Ingredient implements Predicate<ItemStack> {
|
|
if (this.itemStacks == null) {
|
|
this.itemStacks = (ItemStack[]) Arrays.stream(this.values).flatMap((recipeitemstack_provider) -> {
|
|
return recipeitemstack_provider.getItems().stream();
|
|
- }).distinct().toArray((i) -> {
|
|
+ // PaperPR start
|
|
+ }).distinct().peek(stack -> {
|
|
+ stack.isExactRecipeIngredient = this.exact;
|
|
+ }).toArray((i) -> {
|
|
+ // PaperPR end
|
|
return new ItemStack[i];
|
|
});
|
|
}
|
|
@@ -64,6 +69,12 @@ public final class Ingredient implements Predicate<ItemStack> {
|
|
} else if (this.isEmpty()) {
|
|
return itemstack.isEmpty();
|
|
} else {
|
|
+ // Purpur start
|
|
+ if (predicate != null) {
|
|
+ return predicate.test(itemstack.asBukkitCopy());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
ItemStack[] aitemstack = this.getItems();
|
|
int i = aitemstack.length;
|
|
|
|
@@ -99,7 +110,13 @@ public final class Ingredient implements Predicate<ItemStack> {
|
|
for (int j = 0; j < i; ++j) {
|
|
ItemStack itemstack = aitemstack1[j];
|
|
|
|
+ // PaperPR start
|
|
+ if (itemstack.isExactRecipeIngredient) {
|
|
+ this.stackingIds.add(StackedContents.getExactStackingIndex(itemstack));
|
|
+ } else {
|
|
+ // PaperPR end
|
|
this.stackingIds.add(StackedContents.getStackingIndex(itemstack));
|
|
+ } // PaperPR
|
|
}
|
|
|
|
this.stackingIds.sort(IntComparators.NATURAL_COMPARATOR);
|
|
diff --git a/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java b/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java
|
|
index 518d85a13c37a2f7d32ca0718323181048559986..27512787b37381a5236b1b473e9ce3f06df8e2d0 100644
|
|
--- a/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java
|
|
+++ b/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java
|
|
@@ -7,6 +7,14 @@ public class ArrowInfiniteEnchantment extends Enchantment {
|
|
super(weight, EnchantmentCategory.BOW, slotTypes);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean canEnchant(net.minecraft.world.item.ItemStack stack) {
|
|
+ // we have to cheat the system because this class is loaded before purpur's config is loaded
|
|
+ return (org.purpurmc.purpur.PurpurConfig.allowCrossbowInfinity ? EnchantmentCategory.BOW_AND_CROSSBOW : EnchantmentCategory.BOW).canEnchant(stack.getItem());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public int getMinCost(int level) {
|
|
return 20;
|
|
@@ -19,6 +27,6 @@ public class ArrowInfiniteEnchantment extends Enchantment {
|
|
|
|
@Override
|
|
public boolean checkCompatibility(Enchantment other) {
|
|
- return other instanceof MendingEnchantment ? false : super.checkCompatibility(other);
|
|
+ return other instanceof MendingEnchantment ? org.purpurmc.purpur.PurpurConfig.allowInfinityMending : super.checkCompatibility(other);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java
|
|
index 246516e67db0b8b197b287c067d5a0163d8bde22..fc2c35f57436371cb0111aedfd289ac95d506d07 100644
|
|
--- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java
|
|
+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java
|
|
@@ -121,6 +121,20 @@ public enum EnchantmentCategory {
|
|
public boolean canEnchant(Item item) {
|
|
return item instanceof Vanishable || Block.byItem(item) instanceof Vanishable || BREAKABLE.canEnchant(item);
|
|
}
|
|
+ // Purpur start
|
|
+ },
|
|
+ BOW_AND_CROSSBOW {
|
|
+ @Override
|
|
+ public boolean canEnchant(Item item) {
|
|
+ return item instanceof BowItem || item instanceof CrossbowItem;
|
|
+ }
|
|
+ },
|
|
+ WEAPON_AND_SHEARS {
|
|
+ @Override
|
|
+ public boolean canEnchant(Item item) {
|
|
+ return WEAPON.canEnchant(item) || item instanceof net.minecraft.world.item.ShearsItem;
|
|
+ }
|
|
+ // Purpur end
|
|
};
|
|
|
|
public abstract boolean canEnchant(Item item);
|
|
diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
|
index ecf640b00007a386290f8dfe9935a8aa610079fd..1eec84e217f6dc929091fa7451cd235ef3623822 100644
|
|
--- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
|
+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
|
|
@@ -46,7 +46,7 @@ public class EnchantmentHelper {
|
|
}
|
|
|
|
public static int getEnchantmentLevel(CompoundTag nbt) {
|
|
- return Mth.clamp(nbt.getInt("lvl"), 0, 255);
|
|
+ return Mth.clamp(nbt.getInt("lvl"), 0, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels) ? 255 : 32767); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
@@ -278,6 +278,29 @@ public class EnchantmentHelper {
|
|
return getItemEnchantmentLevel(Enchantments.CHANNELING, stack) > 0;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Nullable
|
|
+ public static Map.Entry<EquipmentSlot, ItemStack> getMostDamagedEquipment(Enchantment enchantment, LivingEntity entity) {
|
|
+ Map<EquipmentSlot, ItemStack> map = enchantment.getSlotItems(entity);
|
|
+ if (map.isEmpty()) {
|
|
+ return null;
|
|
+ }
|
|
+ Map.Entry<EquipmentSlot, ItemStack> item = null;
|
|
+ float maxPercent = 0F;
|
|
+ for (Map.Entry<EquipmentSlot, ItemStack> entry : map.entrySet()) {
|
|
+ ItemStack itemstack = entry.getValue();
|
|
+ if (!itemstack.isEmpty() && itemstack.isDamaged() && getItemEnchantmentLevel(enchantment, itemstack) > 0) {
|
|
+ float percent = itemstack.getDamagePercent();
|
|
+ if (item == null || percent > maxPercent) {
|
|
+ item = entry;
|
|
+ maxPercent = percent;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return item;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Nullable
|
|
public static Map.Entry<EquipmentSlot, ItemStack> getRandomItemWith(Enchantment enchantment, LivingEntity entity) {
|
|
return getRandomItemWith(enchantment, entity, (stack) -> {
|
|
diff --git a/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java b/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java
|
|
index 4007c16550683e23b396dfdff29530a82523fe05..8fe09c13643d99639fb242da4367c42ef31b38b4 100644
|
|
--- a/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java
|
|
+++ b/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java
|
|
@@ -7,6 +7,14 @@ public class LootBonusEnchantment extends Enchantment {
|
|
super(weight, target, slotTypes);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean canEnchant(net.minecraft.world.item.ItemStack stack) {
|
|
+ // we have to cheat the system because this class is loaded before purpur's config is loaded
|
|
+ return (org.purpurmc.purpur.PurpurConfig.allowShearsLooting && this.category == EnchantmentCategory.WEAPON ? EnchantmentCategory.WEAPON_AND_SHEARS : this.category).canEnchant(stack.getItem());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public int getMinCost(int level) {
|
|
return 15 + (level - 1) * 9;
|
|
diff --git a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
|
index fd50d1c2435b82215bc5b3fdbe5044d426bc342e..68ffea572045634f1ad67a6954d480e6ae7833f5 100644
|
|
--- a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
|
+++ b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
|
@@ -132,7 +132,12 @@ public class MerchantOffer {
|
|
}
|
|
|
|
public void updateDemand() {
|
|
- this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper
|
|
+ // Purpur start
|
|
+ this.updateDemand(0);
|
|
+ }
|
|
+ public void updateDemand(int minimumDemand) {
|
|
+ this.demand = Math.max(minimumDemand, this.demand + this.uses - (this.maxUses - this.uses));
|
|
+ // Purpur end
|
|
}
|
|
|
|
public ItemStack assemble() {
|
|
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
|
|
index 31ac0e5ca26c7bdfa9b710d0bb78d846ddf6863e..feb65fc9ee04141fe6f77400660442ed207547a1 100644
|
|
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
|
|
@@ -55,6 +55,7 @@ public abstract class BaseSpawner {
|
|
}
|
|
|
|
public boolean isNearPlayer(Level world, BlockPos pos) {
|
|
+ if (world.purpurConfig.spawnerDeactivateByRedstone && world.hasNeighborSignal(pos)) return false; // Purpur
|
|
return world.hasNearbyAlivePlayerThatAffectsSpawning((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (double) this.requiredPlayerRange); // Paper - Affects Spawning API
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java
|
|
index 3b959f42d958bf0f426853aee56753d6c455fcdb..d17abb283ea818244df0379d6b57fc634071e0b9 100644
|
|
--- a/src/main/java/net/minecraft/world/level/EntityGetter.java
|
|
+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java
|
|
@@ -154,7 +154,7 @@ public interface EntityGetter {
|
|
|
|
default boolean hasNearbyAlivePlayer(double x, double y, double z, double range) {
|
|
for(Player player : this.players()) {
|
|
- if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
|
|
+ if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player) && EntitySelector.notAfk.test(player)) {
|
|
double d = player.distanceToSqr(x, y, z);
|
|
if (range < 0.0D || d < range * range) {
|
|
return true;
|
|
diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
|
|
index 185f7b1d4df59f5db7b85b529a2de6402630bf35..7a3d9c1f8abdbc25e88ca35da1546725f0013c68 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Explosion.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
|
|
@@ -86,7 +86,7 @@ public class Explosion {
|
|
this.hitPlayers = Maps.newHashMap();
|
|
this.level = world;
|
|
this.source = entity;
|
|
- this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values
|
|
+ this.radius = (float) (world == null || world.purpurConfig.explosionClampRadius ? Math.max(power, 0.0) : power); // CraftBukkit - clamp bad values // Purpur
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
@@ -137,10 +137,27 @@ public class Explosion {
|
|
|
|
public void explode() {
|
|
// CraftBukkit start
|
|
- if (this.radius < 0.1F) {
|
|
+ if ((this.level == null || this.level.purpurConfig.explosionClampRadius) && this.radius < 0.1F) { // Purpur
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
+
|
|
+ // Purpur start - add PreExplodeEvents
|
|
+ if(this.source != null){
|
|
+ Location location = new Location(this.level.getWorld(), this.x, this.y, this.z);
|
|
+ if(!new org.purpurmc.purpur.event.entity.PreEntityExplodeEvent(this.source.getBukkitEntity(), location, this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F).callEvent()) {
|
|
+ this.wasCanceled = true;
|
|
+ return;
|
|
+ }
|
|
+ }else {
|
|
+ Location location = new Location(this.level.getWorld(), this.x, this.y, this.z);
|
|
+ if(!new org.purpurmc.purpur.event.PreBlockExplodeEvent(location.getBlock(), this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F).callEvent()) {
|
|
+ this.wasCanceled = true;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ //Purpur end
|
|
+
|
|
this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
|
|
Set<BlockPos> set = Sets.newHashSet();
|
|
boolean flag = true;
|
|
@@ -382,7 +399,7 @@ public class Explosion {
|
|
if (!iblockdata.isAir() && iblockdata.isDestroyable()) { // Paper
|
|
BlockPos blockposition1 = blockposition.immutable();
|
|
|
|
- this.level.getProfiler().push("explosion_blocks");
|
|
+ //this.level.getProfiler().push("explosion_blocks"); // Purpur
|
|
if (block.dropFromExplosion(this)) {
|
|
Level world = this.level;
|
|
|
|
@@ -404,7 +421,7 @@ public class Explosion {
|
|
|
|
this.level.setBlock(blockposition, Blocks.AIR.defaultBlockState(), 3);
|
|
block.wasExploded(this.level, blockposition, this);
|
|
- this.level.getProfiler().pop();
|
|
+ //this.level.getProfiler().pop(); // Purpur
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index 6aec1983a0236d6aa0507a2b3ad1c08b3330f0fc..b8001bca2a33ec1e60566948a651400418a6e9e7 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -177,6 +177,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
// Paper end
|
|
|
|
public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
|
|
+ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur
|
|
public final co.aikar.timings.WorldTimingsHandler timings; // Paper
|
|
public static BlockPos lastPhysicsProblem; // Spigot
|
|
private org.spigotmc.TickLimiter entityLimiter;
|
|
@@ -194,6 +195,49 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
// Paper end - fix and optimise world upgrading
|
|
|
|
+ // Purpur start
|
|
+ private com.google.common.cache.Cache<BreedingCooldownPair, Object> playerBreedingCooldowns;
|
|
+
|
|
+ private com.google.common.cache.Cache<BreedingCooldownPair, Object> getNewBreedingCooldownCache() {
|
|
+ return com.google.common.cache.CacheBuilder.newBuilder().expireAfterWrite(this.purpurConfig.animalBreedingCooldownSeconds, java.util.concurrent.TimeUnit.SECONDS).build();
|
|
+ }
|
|
+
|
|
+ public void resetBreedingCooldowns() {
|
|
+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache();
|
|
+ }
|
|
+
|
|
+ public boolean hasBreedingCooldown(java.util.UUID player, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) { // Purpur
|
|
+ return this.playerBreedingCooldowns.getIfPresent(new BreedingCooldownPair(player, animalType)) != null;
|
|
+ }
|
|
+
|
|
+ public void addBreedingCooldown(java.util.UUID player, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) {
|
|
+ this.playerBreedingCooldowns.put(new BreedingCooldownPair(player, animalType), new Object());
|
|
+ }
|
|
+
|
|
+ private static final class BreedingCooldownPair {
|
|
+ private final java.util.UUID playerUUID;
|
|
+ private final Class<? extends net.minecraft.world.entity.animal.Animal> animalType;
|
|
+
|
|
+ public BreedingCooldownPair(java.util.UUID playerUUID, Class<? extends net.minecraft.world.entity.animal.Animal> animalType) {
|
|
+ this.playerUUID = playerUUID;
|
|
+ this.animalType = animalType;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean equals(Object o) {
|
|
+ if (this == o) return true;
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
+ BreedingCooldownPair that = (BreedingCooldownPair) o;
|
|
+ return playerUUID.equals(that.playerUUID) && animalType.equals(that.animalType);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int hashCode() {
|
|
+ return java.util.Objects.hash(playerUUID, animalType);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public CraftWorld getWorld() {
|
|
return this.world;
|
|
}
|
|
@@ -274,7 +318,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
|
|
public abstract ResourceKey<LevelStem> getTypeKey();
|
|
|
|
- protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter
|
|
+ //protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter // Purpur - dont break ABI
|
|
|
|
// Pufferfish start - ensure these get inlined
|
|
private final int minBuildHeight, minSection, height, maxBuildHeight, maxSection;
|
|
@@ -288,6 +332,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor
|
|
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
|
|
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper
|
|
+ this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur
|
|
+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur
|
|
this.generator = gen;
|
|
this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env);
|
|
|
|
@@ -672,9 +718,9 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
BlockState iblockdata2 = this.getBlockState(pos);
|
|
|
|
if ((flags & 128) == 0 && iblockdata2 != iblockdata1 && (iblockdata2.getLightBlock(this, pos) != iblockdata1.getLightBlock(this, pos) || iblockdata2.getLightEmission() != iblockdata1.getLightEmission() || iblockdata2.useShapeForLightOcclusion() || iblockdata1.useShapeForLightOcclusion())) {
|
|
- this.getProfiler().push("queueCheckLight");
|
|
+ //this.getProfiler().push("queueCheckLight"); // Purpur
|
|
this.getChunkSource().getLightEngine().checkBlock(pos);
|
|
- this.getProfiler().pop();
|
|
+ //this.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
/*
|
|
@@ -973,18 +1019,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
|
|
protected void tickBlockEntities() {
|
|
- ProfilerFiller gameprofilerfiller = this.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur
|
|
|
|
- gameprofilerfiller.push("blockEntities");
|
|
- timings.tileEntityPending.startTiming(); // Spigot
|
|
+ //gameprofilerfiller.push("blockEntities"); // Purpur
|
|
+ //timings.tileEntityPending.startTiming(); // Spigot // Purpur
|
|
this.tickingBlockEntities = true;
|
|
if (!this.pendingBlockEntityTickers.isEmpty()) {
|
|
this.blockEntityTickers.addAll(this.pendingBlockEntityTickers);
|
|
this.pendingBlockEntityTickers.clear();
|
|
}
|
|
- timings.tileEntityPending.stopTiming(); // Spigot
|
|
+ //timings.tileEntityPending.stopTiming(); // Spigot // Purpur
|
|
|
|
- timings.tileEntityTick.startTiming(); // Spigot
|
|
+ //timings.tileEntityTick.startTiming(); // Spigot // Purpur
|
|
// Spigot start
|
|
// Iterator iterator = this.blockEntityTickers.iterator();
|
|
int tilesThisCycle = 0;
|
|
@@ -1017,10 +1063,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
this.blockEntityTickers.removeAll(toRemove);
|
|
|
|
- timings.tileEntityTick.stopTiming(); // Spigot
|
|
+ //timings.tileEntityTick.stopTiming(); // Spigot // Purpur
|
|
this.tickingBlockEntities = false;
|
|
co.aikar.timings.TimingHistory.tileEntityTicks += this.blockEntityTickers.size(); // Paper
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
spigotConfig.currentPrimedTnt = 0; // Spigot
|
|
}
|
|
|
|
@@ -1213,7 +1259,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
|
|
@Override
|
|
public List<Entity> getEntities(@Nullable Entity except, AABB box, Predicate<? super Entity> predicate) {
|
|
- this.getProfiler().incrementCounter("getEntities");
|
|
+ //this.getProfiler().incrementCounter("getEntities"); // Purpur
|
|
List<Entity> list = Lists.newArrayList();
|
|
((ServerLevel)this).getEntityLookup().getEntities(except, box, list, predicate); // Paper - optimise this call
|
|
return list;
|
|
@@ -1232,7 +1278,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
|
|
public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> filter, AABB box, Predicate<? super T> predicate, List<? super T> result, int limit) {
|
|
- this.getProfiler().incrementCounter("getEntities");
|
|
+ //this.getProfiler().incrementCounter("getEntities"); // Purpur
|
|
// Paper start - optimise this call
|
|
//TODO use limit
|
|
if (filter instanceof net.minecraft.world.entity.EntityType entityTypeTest) {
|
|
@@ -1557,7 +1603,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
|
|
public ProfilerFiller getProfiler() {
|
|
- if (gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish
|
|
+ if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish
|
|
return (ProfilerFiller) this.profiler.get();
|
|
}
|
|
|
|
@@ -1648,4 +1694,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
return null;
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ public boolean isNether() {
|
|
+ return getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER;
|
|
+ }
|
|
+
|
|
+ public boolean isTheEnd() {
|
|
+ return getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
index 6180679d922ea61d05d452971ec2d506a724d3c3..132041362b2707946bd386c88bbdf871a317afb7 100644
|
|
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -132,8 +132,8 @@ public final class NaturalSpawner {
|
|
}
|
|
|
|
public static void spawnForChunk(ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rareSpawn) {
|
|
- world.getProfiler().push("spawner");
|
|
- world.timings.mobSpawn.startTiming(); // Spigot
|
|
+ //world.getProfiler().push("spawner"); // Purpur
|
|
+ //world.timings.mobSpawn.startTiming(); // Spigot // Purpur
|
|
MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES;
|
|
int i = aenumcreaturetype.length;
|
|
|
|
@@ -188,8 +188,8 @@ public final class NaturalSpawner {
|
|
}
|
|
}
|
|
|
|
- world.timings.mobSpawn.stopTiming(); // Spigot
|
|
- world.getProfiler().pop();
|
|
+ //world.timings.mobSpawn.stopTiming(); // Spigot // Purpur
|
|
+ //world.getProfiler().pop(); // Purpur
|
|
}
|
|
|
|
// Paper start
|
|
@@ -260,7 +260,7 @@ public final class NaturalSpawner {
|
|
blockposition_mutableblockposition.set(l, i, i1);
|
|
double d0 = (double) l + 0.5D;
|
|
double d1 = (double) i1 + 0.5D;
|
|
- Player entityhuman = (chunk instanceof LevelChunk) ? ((LevelChunk)chunk).findNearestPlayer(d0, i, d1, 576.0D, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS) : world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); // Paper - use chunk's player cache to optimize search in range
|
|
+ Player entityhuman = (chunk instanceof LevelChunk) ? ((LevelChunk)chunk).findNearestPlayer(d0, i, d1, 576.0D, world.purpurConfig.mobSpawningIgnoreCreativePlayers ? net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR : net.minecraft.world.entity.EntitySelector.NO_SPECTATORS) : world.getNearestPlayer(d0, (double) i, d1, -1.0D, world.purpurConfig.mobSpawningIgnoreCreativePlayers); // Paper - use chunk's player cache to optimize search in range // Purpur
|
|
|
|
if (entityhuman != null) {
|
|
double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
|
|
index 5c5a3b169795bf8a527b316c666cbc2105c66622..020afeca950d2c7fb6c7b179d424548fd90f8b0d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
|
|
@@ -55,6 +55,54 @@ public class AnvilBlock extends FallingBlock {
|
|
|
|
@Override
|
|
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
|
+ // Purpur start - repairable/damageable anvils
|
|
+ if (world.purpurConfig.anvilRepairIngotsAmount > 0) {
|
|
+ net.minecraft.world.item.ItemStack itemstack = player.getItemInHand(hand);
|
|
+ if (itemstack.is(net.minecraft.world.item.Items.IRON_INGOT)) {
|
|
+ if (itemstack.getCount() < world.purpurConfig.anvilRepairIngotsAmount) {
|
|
+ // not enough iron ingots, play "error" sound and consume
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return InteractionResult.CONSUME;
|
|
+ }
|
|
+ if (state.is(Blocks.DAMAGED_ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ } else if (state.is(Blocks.CHIPPED_ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ } else if (state.is(Blocks.ANVIL)) {
|
|
+ // anvil is already fully repaired, play "error" sound and consume
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return InteractionResult.CONSUME;
|
|
+ }
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(world.purpurConfig.anvilRepairIngotsAmount);
|
|
+ }
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_PLACE, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return InteractionResult.CONSUME;
|
|
+ }
|
|
+ }
|
|
+ if (world.purpurConfig.anvilDamageObsidianAmount > 0) {
|
|
+ net.minecraft.world.item.ItemStack itemstack = player.getItemInHand(hand);
|
|
+ if (itemstack.is(net.minecraft.world.item.Items.OBSIDIAN)) {
|
|
+ if (itemstack.getCount() < world.purpurConfig.anvilDamageObsidianAmount) {
|
|
+ // not enough obsidian, play "error" sound and consume
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return InteractionResult.CONSUME;
|
|
+ }
|
|
+ if (state.is(Blocks.DAMAGED_ANVIL)) {
|
|
+ world.destroyBlock(pos, false);
|
|
+ } else if (state.is(Blocks.CHIPPED_ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.DAMAGED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ } else if (state.is(Blocks.ANVIL)) {
|
|
+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3);
|
|
+ }
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(world.purpurConfig.anvilDamageObsidianAmount);
|
|
+ }
|
|
+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_LAND, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
+ return InteractionResult.CONSUME;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
if (world.isClientSide) {
|
|
return InteractionResult.SUCCESS;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java b/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java
|
|
index 95b53450a807fccfa55b59852da52785b8cf3e3d..c69f1d23979a0759472d22760a18d986b2d979b6 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java
|
|
@@ -43,6 +43,20 @@ public class AzaleaBlock extends BushBlock implements BonemealableBlock {
|
|
|
|
@Override
|
|
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ // Purpur start
|
|
+ growTree(world, random, pos, state);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void randomTick(net.minecraft.world.level.block.state.BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
|
|
+ double chance = state.getBlock() == Blocks.FLOWERING_AZALEA ? world.purpurConfig.floweringAzaleaGrowthChance : world.purpurConfig.azaleaGrowthChance;
|
|
+ if (chance > 0.0D && world.getMaxLocalRawBrightness(pos.above()) > 9 && random.nextDouble() < chance) {
|
|
+ growTree(world, random, pos, state);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void growTree(ServerLevel world, RandomSource random, BlockPos pos, net.minecraft.world.level.block.state.BlockState state) {
|
|
+ // Purpur end
|
|
TREE_GROWER.growTree(world, world.getChunkSource().getGenerator(), pos, state, random);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java b/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
|
index 3d2b34c5a7c9b00c1164b4f89c2cbff81fc460eb..b5505e926e5cdb447de68e8eb8e46c97eb988e27 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java
|
|
@@ -35,6 +35,7 @@ public class BaseCoralPlantTypeBlock extends Block implements SimpleWaterloggedB
|
|
}
|
|
|
|
protected static boolean scanForWater(BlockState state, BlockGetter world, BlockPos pos) {
|
|
+ if (!((net.minecraft.world.level.LevelAccessor) world).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur
|
|
if (state.getValue(WATERLOGGED)) {
|
|
return true;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BedBlock.java b/src/main/java/net/minecraft/world/level/block/BedBlock.java
|
|
index d1d5363ab1742add8ff45507a303106f4d65f52f..19d31064eb271ee02115a75cde383796c899e7f7 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BedBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java
|
|
@@ -97,7 +97,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock
|
|
|
|
Vec3 vec3d = pos.getCenter();
|
|
|
|
- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK);
|
|
+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // Purpur
|
|
return InteractionResult.SUCCESS;
|
|
} else if ((Boolean) state.getValue(BedBlock.OCCUPIED)) {
|
|
if (!BedBlock.canSetSpawn(world)) return this.explodeBed(state, world, pos); // Paper - check explode first
|
|
@@ -150,7 +150,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock
|
|
|
|
Vec3 vec3d = blockposition.getCenter();
|
|
|
|
- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK);
|
|
+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // Purpur
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
}
|
|
@@ -174,7 +174,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock
|
|
|
|
@Override
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
- super.fallOn(world, state, pos, entity, fallDistance * 0.5F);
|
|
+ super.fallOn(world, state, pos, entity, fallDistance); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java
|
|
index 8537581e7ca1f4efb492a2e734f46f947f36cffa..5f89229ff68d923c5cdee40e72e379ba7024f961 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java
|
|
@@ -236,7 +236,7 @@ public class BigDripleafBlock extends HorizontalDirectionalBlock implements Bone
|
|
BigDripleafBlock.playTiltSound(world, blockposition, soundeffect);
|
|
}
|
|
|
|
- int i = BigDripleafBlock.DELAY_UNTIL_NEXT_TILT_STATE.getInt(tilt);
|
|
+ int i = world.purpurConfig.bigDripleafTiltDelay.getOrDefault(tilt, -1); // Purpur
|
|
|
|
if (i != -1) {
|
|
world.scheduleTick(blockposition, (Block) this, i);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
|
|
index 4f91e4832a94c3facbc711fcae4cb5ad540a5ca0..773162c3456945605fb664114508622f7d2fcec8 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/Block.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
|
|
@@ -63,6 +63,13 @@ import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.slf4j.Logger;
|
|
|
|
+// Purpur start
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.nbt.ListTag;
|
|
+import net.minecraft.nbt.StringTag;
|
|
+import net.minecraft.world.Nameable;
|
|
+// Purpur end
|
|
+
|
|
public class Block extends BlockBehaviour implements ItemLike {
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
@@ -89,6 +96,10 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
public static final int UPDATE_LIMIT = 512;
|
|
protected final StateDefinition<Block, BlockState> stateDefinition;
|
|
private BlockState defaultBlockState;
|
|
+ // Purpur start
|
|
+ public float fallDamageMultiplier = 1.0F;
|
|
+ public float fallDistanceMultiplier = 1.0F;
|
|
+ // Purpur end
|
|
// Paper start
|
|
public final boolean isDestroyable() {
|
|
return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits ||
|
|
@@ -325,7 +336,7 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
public static void dropResources(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockEntity blockEntity) {
|
|
if (world instanceof ServerLevel) {
|
|
Block.getDrops(state, (ServerLevel) world, pos, blockEntity).forEach((itemstack) -> {
|
|
- Block.popResource((ServerLevel) world, pos, itemstack);
|
|
+ Block.popResource((ServerLevel) world, pos, applyDisplayNameAndLoreFromTile(itemstack, blockEntity)); // Purpur
|
|
});
|
|
state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true);
|
|
}
|
|
@@ -341,7 +352,7 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
io.papermc.paper.event.block.BlockBreakBlockEvent event = new io.papermc.paper.event.block.BlockBreakBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, pos), org.bukkit.craftbukkit.block.CraftBlock.at(world, source), items);
|
|
event.callEvent();
|
|
for (var drop : event.getDrops()) {
|
|
- popResource(world.getMinecraftWorld(), pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop));
|
|
+ popResource(world.getMinecraftWorld(), pos, applyDisplayNameAndLoreFromTile(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop), blockEntity)); // Purpur
|
|
}
|
|
state.spawnAfterBreak(world.getMinecraftWorld(), pos, ItemStack.EMPTY, true);
|
|
}
|
|
@@ -352,13 +363,53 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, Entity entity, ItemStack tool) {
|
|
if (world instanceof ServerLevel) {
|
|
Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> {
|
|
- Block.popResource(world, pos, itemstack1);
|
|
+ Block.popResource(world, pos, applyDisplayNameAndLoreFromTile(itemstack1, blockEntity)); // Purpur
|
|
});
|
|
state.spawnAfterBreak((ServerLevel) world, pos, tool, true);
|
|
}
|
|
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ private static ItemStack applyDisplayNameAndLoreFromTile(ItemStack stack, BlockEntity blockEntity) {
|
|
+ if (stack.getItem() instanceof BlockItem) {
|
|
+ if (blockEntity != null && blockEntity.getLevel() instanceof ServerLevel && blockEntity.getLevel().purpurConfig.persistentTileEntityDisplayNames) {
|
|
+ String name = blockEntity.getPersistentDisplayName();
|
|
+ ListTag lore = blockEntity.getPersistentLore();
|
|
+ if (blockEntity instanceof Nameable) {
|
|
+ Nameable namedTile = (Nameable) blockEntity;
|
|
+ if (namedTile.hasCustomName()) {
|
|
+ name = Component.Serializer.toJson(namedTile.getCustomName());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (name != null || lore != null) {
|
|
+ CompoundTag display = stack.getTagElement("display");
|
|
+ if (display == null) {
|
|
+ display = new CompoundTag();
|
|
+ }
|
|
+
|
|
+ if (name != null) {
|
|
+ display.put("Name", StringTag.valueOf(name));
|
|
+ }
|
|
+ if (lore != null) {
|
|
+ display.put("Lore", lore);
|
|
+ }
|
|
+
|
|
+ CompoundTag tag = stack.getTag();
|
|
+ if (tag == null) {
|
|
+ tag = new CompoundTag();
|
|
+ }
|
|
+ tag.put("display", display);
|
|
+
|
|
+ stack.setTag(tag);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return stack;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static void popResource(Level world, BlockPos pos, ItemStack stack) {
|
|
double d0 = (double) EntityType.ITEM.getHeight() / 2.0D;
|
|
double d1 = (double) pos.getX() + 0.5D + Mth.nextDouble(world.random, -0.25D, 0.25D);
|
|
@@ -434,7 +485,17 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
Block.dropResources(state, world, pos, blockEntity, player, tool);
|
|
}
|
|
|
|
- public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {}
|
|
+ // Purpur start
|
|
+ @Nullable protected LivingEntity placer = null;
|
|
+
|
|
+ public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {
|
|
+ this.placer = placer;
|
|
+ }
|
|
+
|
|
+ public void forgetPlacer() {
|
|
+ this.placer = null;
|
|
+ }
|
|
+ // Purpur end
|
|
|
|
public boolean isPossibleToRespawnInThis() {
|
|
return !this.material.isSolid() && !this.material.isLiquid();
|
|
@@ -453,7 +514,7 @@ public class Block extends BlockBehaviour implements ItemLike {
|
|
}
|
|
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
- entity.causeFallDamage(fallDistance, 1.0F, entity.damageSources().fall());
|
|
+ entity.causeFallDamage(fallDistance * fallDistanceMultiplier, fallDamageMultiplier, entity.damageSources().fall()); // Purpur
|
|
}
|
|
|
|
public void updateEntityAfterFallOn(BlockGetter world, Entity entity) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/Blocks.java b/src/main/java/net/minecraft/world/level/block/Blocks.java
|
|
index f148c7d2954cc17377d0da4af03ea2c1c9397a52..4afc4670f9b00a4087410ec366fe45fe2f2734dc 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/Blocks.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/Blocks.java
|
|
@@ -1087,8 +1087,8 @@ public class Blocks {
|
|
public static final Block CAVE_VINES = register("cave_vines", new CaveVinesBlock(BlockBehaviour.Properties.of(Material.PLANT).randomTicks().noCollission().lightLevel(CaveVines.emission(14)).instabreak().sound(SoundType.CAVE_VINES)));
|
|
public static final Block CAVE_VINES_PLANT = register("cave_vines_plant", new CaveVinesPlantBlock(BlockBehaviour.Properties.of(Material.PLANT).noCollission().lightLevel(CaveVines.emission(14)).instabreak().sound(SoundType.CAVE_VINES)));
|
|
public static final Block SPORE_BLOSSOM = register("spore_blossom", new SporeBlossomBlock(BlockBehaviour.Properties.of(Material.PLANT).instabreak().noCollission().sound(SoundType.SPORE_BLOSSOM)));
|
|
- public static final Block AZALEA = register("azalea", new AzaleaBlock(BlockBehaviour.Properties.of(Material.PLANT).instabreak().sound(SoundType.AZALEA).noOcclusion()));
|
|
- public static final Block FLOWERING_AZALEA = register("flowering_azalea", new AzaleaBlock(BlockBehaviour.Properties.of(Material.PLANT).instabreak().sound(SoundType.FLOWERING_AZALEA).noOcclusion()));
|
|
+ public static final Block AZALEA = register("azalea", new AzaleaBlock(BlockBehaviour.Properties.of(Material.PLANT).randomTicks().instabreak().sound(SoundType.AZALEA).noOcclusion())); // Purpur
|
|
+ public static final Block FLOWERING_AZALEA = register("flowering_azalea", new AzaleaBlock(BlockBehaviour.Properties.of(Material.PLANT).randomTicks().instabreak().sound(SoundType.FLOWERING_AZALEA).noOcclusion())); // Purpur
|
|
public static final Block MOSS_CARPET = register("moss_carpet", new CarpetBlock(BlockBehaviour.Properties.of(Material.PLANT, MaterialColor.COLOR_GREEN).strength(0.1F).sound(SoundType.MOSS_CARPET)));
|
|
public static final Block PINK_PETALS = register("pink_petals", new PinkPetalsBlock(BlockBehaviour.Properties.of(Material.PLANT).noCollission().sound(SoundType.PINK_PETALS).requiredFeatures(FeatureFlags.UPDATE_1_20)));
|
|
public static final Block MOSS_BLOCK = register("moss_block", new MossBlock(BlockBehaviour.Properties.of(Material.MOSS, MaterialColor.COLOR_GREEN).strength(0.1F).sound(SoundType.MOSS)));
|
|
@@ -1153,7 +1153,7 @@ public class Blocks {
|
|
}
|
|
|
|
private static Boolean ocelotOrParrot(BlockState state, BlockGetter world, BlockPos pos, EntityType<?> type) {
|
|
- return (boolean)type == EntityType.OCELOT || type == EntityType.PARROT;
|
|
+ return type == EntityType.OCELOT || type == EntityType.PARROT; // Purpur - decompile error
|
|
}
|
|
|
|
private static BedBlock bed(DyeColor color) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BuddingAmethystBlock.java b/src/main/java/net/minecraft/world/level/block/BuddingAmethystBlock.java
|
|
index bedccb8717d08d5a60058445b04ddff149e7d36c..5293ffca3da94c9c485a87d1232b6a902fcafd6a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BuddingAmethystBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BuddingAmethystBlock.java
|
|
@@ -53,4 +53,14 @@ public class BuddingAmethystBlock extends AmethystBlock {
|
|
public static boolean canClusterGrowAtState(BlockState state) {
|
|
return state.isAir() || state.is(Blocks.WATER) && state.getFluidState().getAmount() == 8;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void playerDestroy(net.minecraft.world.level.Level level, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, net.minecraft.world.level.block.entity.BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
|
|
+ if (level.purpurConfig.buddingAmethystSilkTouch && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.SILK_TOUCH, stack) > 0) {
|
|
+ popResource(level, pos, net.minecraft.world.item.Items.BUDDING_AMETHYST.getDefaultInstance());
|
|
+ }
|
|
+ super.playerDestroy(level, player, pos, state, blockEntity, stack);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/BushBlock.java b/src/main/java/net/minecraft/world/level/block/BushBlock.java
|
|
index 03fde6e47c4a347c62fe9b4a3351769aedf874f6..ca906b0250e5332f7ececf1419ca6d2c1d385adc 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/BushBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/BushBlock.java
|
|
@@ -48,4 +48,24 @@ public class BushBlock extends Block {
|
|
public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) {
|
|
return type == PathComputationType.AIR && !this.hasCollision ? true : super.isPathfindable(state, world, pos, type);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public void playerDestroyAndReplant(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, net.minecraft.world.item.ItemStack itemInHand, net.minecraft.world.level.ItemLike itemToReplant) {
|
|
+ player.awardStat(net.minecraft.stats.Stats.BLOCK_MINED.get(this));
|
|
+ player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED);
|
|
+ java.util.List<net.minecraft.world.item.ItemStack> dropList = Block.getDrops(state, (net.minecraft.server.level.ServerLevel) world, pos, blockEntity, player, itemInHand);
|
|
+
|
|
+ boolean planted = false;
|
|
+ for (net.minecraft.world.item.ItemStack itemToDrop : dropList) {
|
|
+ if (!planted && itemToDrop.getItem() == itemToReplant) {
|
|
+ world.setBlock(pos, defaultBlockState(), 3);
|
|
+ itemToDrop.setCount(itemToDrop.getCount() - 1);
|
|
+ planted = true;
|
|
+ }
|
|
+ Block.popResource(world, pos, itemToDrop);
|
|
+ }
|
|
+
|
|
+ state.spawnAfterBreak((net.minecraft.server.level.ServerLevel) world, pos, itemInHand, true);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CactusBlock.java b/src/main/java/net/minecraft/world/level/block/CactusBlock.java
|
|
index 7579946ce222b6ab3685a7fd9821bcd5a4babe33..ae2ac1c24c1e502a1968a3008273096281d5f1ca 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CactusBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CactusBlock.java
|
|
@@ -22,7 +22,7 @@ import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
|
|
|
-public class CactusBlock extends Block {
|
|
+public class CactusBlock extends Block implements BonemealableBlock { // Purpur
|
|
|
|
public static final IntegerProperty AGE = BlockStateProperties.AGE_15;
|
|
public static final int MAX_AGE = 15;
|
|
@@ -109,7 +109,7 @@ public class CactusBlock extends Block {
|
|
BlockState iblockdata2 = world.getBlockState(pos.relative(enumdirection));
|
|
|
|
material = iblockdata2.getMaterial();
|
|
- } while (!material.isSolid() && !world.getFluidState(pos.relative(enumdirection)).is(FluidTags.LAVA));
|
|
+ } while ((!world.getWorldBorder().world.purpurConfig.cactusBreaksFromSolidNeighbors || !material.isSolid()) && !world.getFluidState(pos.relative(enumdirection)).is(FluidTags.LAVA)); // Purpur
|
|
|
|
return false;
|
|
}
|
|
@@ -131,4 +131,34 @@ public class CactusBlock extends Block {
|
|
public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) {
|
|
return false;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isValidBonemealTarget(LevelReader world, BlockPos pos, BlockState state, boolean isClient) {
|
|
+ if (!((Level) world).purpurConfig.cactusAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false;
|
|
+
|
|
+ int cactusHeight = 0;
|
|
+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) {
|
|
+ cactusHeight++;
|
|
+ }
|
|
+
|
|
+ return cactusHeight < ((Level) world).paperConfig().maxGrowthHeight.cactus;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isBonemealSuccess(Level world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ int cactusHeight = 0;
|
|
+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) {
|
|
+ cactusHeight++;
|
|
+ }
|
|
+ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.cactus - cactusHeight; i++) {
|
|
+ world.setBlockAndUpdate(pos.above(i), state.setValue(CactusBlock.AGE, 0));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java
|
|
index 219c87dcf065e86512f330fbeec59e55f4675083..f8fd3b320494d1c1e8ee3d170f2feebd152230fa 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java
|
|
@@ -122,7 +122,7 @@ public class CampfireBlock extends BaseEntityBlock implements SimpleWaterloggedB
|
|
BlockPos blockposition = ctx.getClickedPos();
|
|
boolean flag = world.getFluidState(blockposition).getType() == Fluids.WATER;
|
|
|
|
- return (BlockState) ((BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(CampfireBlock.WATERLOGGED, flag)).setValue(CampfireBlock.SIGNAL_FIRE, this.isSmokeSource(world.getBlockState(blockposition.below())))).setValue(CampfireBlock.LIT, !flag)).setValue(CampfireBlock.FACING, ctx.getHorizontalDirection());
|
|
+ return (BlockState) ((BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(CampfireBlock.WATERLOGGED, flag)).setValue(CampfireBlock.SIGNAL_FIRE, this.isSmokeSource(world.getBlockState(blockposition.below())))).setValue(CampfireBlock.LIT, world.purpurConfig.campFireLitWhenPlaced ? !flag : world.purpurConfig.campFireLitWhenPlaced)).setValue(CampfireBlock.FACING, ctx.getHorizontalDirection()); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java b/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
|
index 05112bc416019daba885a3de1b7f96177665135f..32d7ae44dd4e4987b1085f08cb30a92937e57226 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java
|
|
@@ -69,7 +69,7 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock implements Eq
|
|
SnowGolem entitysnowman = (SnowGolem) EntityType.SNOW_GOLEM.create(world);
|
|
|
|
if (entitysnowman != null) {
|
|
- CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection, entitysnowman, shapedetector_shapedetectorcollection.getBlock(0, 2, 0).getPos());
|
|
+ CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection, entitysnowman, shapedetector_shapedetectorcollection.getBlock(0, 2, 0).getPos(), this.placer); // Purpur
|
|
}
|
|
} else {
|
|
BlockPattern.BlockPatternMatch shapedetector_shapedetectorcollection1 = this.getOrCreateIronGolemFull().find(world, pos);
|
|
@@ -79,7 +79,7 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock implements Eq
|
|
|
|
if (entityirongolem != null) {
|
|
entityirongolem.setPlayerCreated(true);
|
|
- CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection1, entityirongolem, shapedetector_shapedetectorcollection1.getBlock(1, 2, 0).getPos());
|
|
+ CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection1, entityirongolem, shapedetector_shapedetectorcollection1.getBlock(1, 2, 0).getPos(), this.placer); // Purpur
|
|
}
|
|
}
|
|
}
|
|
@@ -87,6 +87,16 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock implements Eq
|
|
}
|
|
|
|
private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos) {
|
|
+ // Purpur start
|
|
+ spawnGolemInWorld(world, patternResult, entity, pos, null);
|
|
+ }
|
|
+ private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos, net.minecraft.world.entity.LivingEntity placer) {
|
|
+ if (entity instanceof SnowGolem snowGolem) {
|
|
+ snowGolem.setSummoner(placer == null ? null : placer.getUUID());
|
|
+ } else if (entity instanceof IronGolem ironGolem) {
|
|
+ ironGolem.setSummoner(placer == null ? null : placer.getUUID());
|
|
+ }
|
|
+ // Purpur end
|
|
// clearPatternBlocks(world, shapedetector_shapedetectorcollection); // CraftBukkit - moved down
|
|
entity.moveTo((double) pos.getX() + 0.5D, (double) pos.getY() + 0.05D, (double) pos.getZ() + 0.5D, 0.0F, 0.0F);
|
|
// CraftBukkit start
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java
|
|
index 2f85b893dd0abc39fcedec65acc89e1567faf6f0..3ee012a9ef8cada0b2203e53b2f731f60f697cb1 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java
|
|
@@ -29,7 +29,7 @@ public class CauldronBlock extends AbstractCauldronBlock {
|
|
}
|
|
|
|
protected static boolean shouldHandlePrecipitation(Level world, Biome.Precipitation precipitation) {
|
|
- return precipitation == Biome.Precipitation.RAIN ? world.getRandom().nextFloat() < 0.05F : (precipitation == Biome.Precipitation.SNOW ? world.getRandom().nextFloat() < 0.1F : false);
|
|
+ return precipitation == Biome.Precipitation.RAIN ? world.getRandom().nextFloat() < world.purpurConfig.cauldronRainChance : (precipitation == Biome.Precipitation.SNOW ? world.getRandom().nextFloat() < world.purpurConfig.cauldronPowderSnowChance : false); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
|
index 18b5bce1138d50be32e5da013221be69dc47e21f..58b4a0d97af37f7164db86ef821f04102c6c5ddd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java
|
|
@@ -88,4 +88,11 @@ public class CaveVinesBlock extends GrowingPlantHeadBlock implements Bonemealabl
|
|
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
world.setBlock(pos, state.setValue(BERRIES, Boolean.valueOf(true)), 2);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.caveVinesMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ChestBlock.java b/src/main/java/net/minecraft/world/level/block/ChestBlock.java
|
|
index 5e22d175b1048a58802cdf64ac70a8b56329e915..d81946b400f208c39941128ce823ff7709741c10 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ChestBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ChestBlock.java
|
|
@@ -355,6 +355,7 @@ public class ChestBlock extends AbstractChestBlock<ChestBlockEntity> implements
|
|
}
|
|
|
|
private static boolean isBlockedChestByBlock(BlockGetter world, BlockPos pos) {
|
|
+ if (world instanceof Level && ((Level) world).purpurConfig.chestOpenWithBlockOnTop) return false; // Purpur
|
|
BlockPos blockposition1 = pos.above();
|
|
|
|
return world.getBlockState(blockposition1).isRedstoneConductor(world, blockposition1);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ChorusPlantBlock.java b/src/main/java/net/minecraft/world/level/block/ChorusPlantBlock.java
|
|
index a6c25647fb37f59307de0d390f8e8cf55504d7d3..52aae8bd4023b2bb48f12983f54b20fa3c95d403 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ChorusPlantBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ChorusPlantBlock.java
|
|
@@ -21,6 +21,7 @@ public class ChorusPlantBlock extends PipeBlock {
|
|
|
|
@Override
|
|
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableChorusPlantUpdates) return this.defaultBlockState(); // Purpur
|
|
return this.getStateForPlacement(ctx.getLevel(), ctx.getClickedPos());
|
|
}
|
|
|
|
@@ -36,6 +37,7 @@ public class ChorusPlantBlock extends PipeBlock {
|
|
|
|
@Override
|
|
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableChorusPlantUpdates) return state; // Purpur
|
|
if (!state.canSurvive(world, pos)) {
|
|
world.scheduleTick(pos, this, 1);
|
|
return super.updateShape(state, direction, neighborState, world, pos, neighborPos);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java
|
|
index 6fab2b69a0af298bd00b309efcd6aa8399e23d1f..4f7b21caa123ea7896788fd25133d8de3ab1ccaf 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java
|
|
@@ -228,20 +228,28 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
|
|
ItemStack itemstack = player.getItemInHand(hand);
|
|
|
|
if (i < 8 && ComposterBlock.COMPOSTABLES.containsKey(itemstack.getItem())) {
|
|
- if (i < 7 && !world.isClientSide) {
|
|
- BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, itemstack);
|
|
- // Paper start - handle cancelled events
|
|
- if (iblockdata1 == null) {
|
|
- return InteractionResult.PASS;
|
|
- }
|
|
- // Paper end
|
|
+ // Purpur start
|
|
+ BlockState newState = process(i, state, world, itemstack, pos, player);
|
|
+ if (newState == null) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
|
|
- world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0);
|
|
- player.awardStat(Stats.ITEM_USED.get(itemstack.getItem()));
|
|
- if (!player.getAbilities().instabuild) {
|
|
- itemstack.shrink(1);
|
|
- }
|
|
+ if (world.purpurConfig.composterBulkProcess && player.isShiftKeyDown() && newState != state) {
|
|
+ BlockState oldState;
|
|
+ int oldCount, newCount, oldLevel, newLevel;
|
|
+ do {
|
|
+ oldState = newState;
|
|
+ oldCount = itemstack.getCount();
|
|
+ oldLevel = oldState.getValue(ComposterBlock.LEVEL);
|
|
+ newState = process(oldLevel, oldState, world, itemstack, pos, player);
|
|
+ if (newState == null) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ newCount = itemstack.getCount();
|
|
+ newLevel = newState.getValue(ComposterBlock.LEVEL);
|
|
+ } while (newCount > 0 && (newCount != oldCount || newLevel != oldLevel || newState != oldState));
|
|
}
|
|
+ // Purpur end
|
|
|
|
return InteractionResult.sidedSuccess(world.isClientSide);
|
|
} else if (i == 8) {
|
|
@@ -252,6 +260,26 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
|
|
}
|
|
}
|
|
|
|
+ private static BlockState process(int level, BlockState state, Level world, ItemStack itemstack, BlockPos pos, Player player) {
|
|
+ if (level < 7 && !world.isClientSide) {
|
|
+ BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, itemstack);
|
|
+ // Paper start - handle cancelled events
|
|
+ if (iblockdata1 == null) {
|
|
+ return iblockdata1;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0);
|
|
+ player.awardStat(Stats.ITEM_USED.get(itemstack.getItem()));
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ itemstack.shrink(1);
|
|
+ }
|
|
+ return iblockdata1;
|
|
+ }
|
|
+ return state;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
public static BlockState insertItem(Entity user, BlockState state, ServerLevel world, ItemStack stack, BlockPos pos) {
|
|
int i = (Integer) state.getValue(ComposterBlock.LEVEL);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CoralBlock.java b/src/main/java/net/minecraft/world/level/block/CoralBlock.java
|
|
index 88faea00be60a519f56f975a5311df5e1eb3e6b8..cbb726ac367be81e27d3a86643baf7c4f0746edf 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CoralBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CoralBlock.java
|
|
@@ -45,6 +45,7 @@ public class CoralBlock extends Block {
|
|
}
|
|
|
|
protected boolean scanForWater(BlockGetter world, BlockPos pos) {
|
|
+ if (!((net.minecraft.world.level.LevelAccessor) world).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur
|
|
Direction[] aenumdirection = Direction.values();
|
|
int i = aenumdirection.length;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java
|
|
index a140fed067e7e6c1c42e111f47d3678863ef95ce..3415cbb1def0700b5998a8a1db2e48146f4c2c1e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/CropBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java
|
|
@@ -168,7 +168,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock {
|
|
@Override
|
|
public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
|
|
if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper
|
|
- if (entity instanceof Ravager && !CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)).isCancelled()) { // CraftBukkit
|
|
+ if (entity instanceof Ravager && world.purpurConfig.ravagerGriefableBlocks.contains(world.getBlockState(pos).getBlock()) && !CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), (!world.purpurConfig.ravagerBypassMobGriefing && !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))).isCancelled()) { // CraftBukkit // Purpur
|
|
world.destroyBlock(pos, true, entity);
|
|
}
|
|
|
|
@@ -203,4 +203,15 @@ public class CropBlock extends BushBlock implements BonemealableBlock {
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(CropBlock.AGE);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void playerDestroy(Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand) {
|
|
+ if (world.purpurConfig.hoeReplantsCrops && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) {
|
|
+ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, getBaseSeedId());
|
|
+ } else {
|
|
+ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/DoorBlock.java b/src/main/java/net/minecraft/world/level/block/DoorBlock.java
|
|
index 5ba56ee7d5dd210770e6703be559055d218028d5..b5e90dc00240bccf1a6eca342729a4f4165e22bf 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/DoorBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/DoorBlock.java
|
|
@@ -165,6 +165,7 @@ public class DoorBlock extends Block {
|
|
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
|
if (this.material == Material.METAL) {
|
|
return InteractionResult.PASS;
|
|
+ } else if (requiresRedstone(world, state, pos)) { return InteractionResult.CONSUME; // Purpur
|
|
} else {
|
|
state = (BlockState) state.cycle(DoorBlock.OPEN);
|
|
world.setBlock(pos, state, 10);
|
|
@@ -260,4 +261,18 @@ public class DoorBlock extends Block {
|
|
public static boolean isWoodenDoor(BlockState state) {
|
|
return state.getBlock() instanceof DoorBlock && (state.getMaterial() == Material.WOOD || state.getMaterial() == Material.NETHER_WOOD);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public static boolean requiresRedstone(Level level, BlockState state, BlockPos pos) {
|
|
+ if (level.purpurConfig.doorRequiresRedstone.contains(state.getBlock())) {
|
|
+ // force update client
|
|
+ BlockPos otherPos = pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN);
|
|
+ BlockState otherState = level.getBlockState(otherPos);
|
|
+ level.sendBlockUpdated(pos, state, state, 3);
|
|
+ level.sendBlockUpdated(otherPos, otherState, otherState, 3);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java b/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java
|
|
index 7e1edcc7b9f170b7c649437c2f0dd78c0bab9be4..5f8ac1fdac2c334951261f2b9702f5e711743c88 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java
|
|
@@ -42,8 +42,8 @@ public class DragonEggBlock extends FallingBlock {
|
|
}
|
|
|
|
private void teleport(BlockState state, Level world, BlockPos pos) {
|
|
+ if (!world.purpurConfig.dragonEggTeleport) return; // Purpur
|
|
WorldBorder worldborder = world.getWorldBorder();
|
|
-
|
|
for (int i = 0; i < 1000; ++i) {
|
|
BlockPos blockposition1 = pos.offset(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16));
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java b/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java
|
|
index f4ee3ce287528337a0f9a3b612c157254f895a58..c4a91d7f1320027ee6a2b364303c01ebbacde584 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java
|
|
@@ -28,6 +28,8 @@ import net.minecraft.world.level.pathfinder.PathComputationType;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
+import net.minecraft.world.Containers; // Purpur
|
|
+import net.minecraft.world.item.Items; // Purpur
|
|
|
|
public class EnchantmentTableBlock extends BaseEntityBlock {
|
|
protected static final VoxelShape SHAPE = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
|
@@ -40,6 +42,10 @@ public class EnchantmentTableBlock extends BaseEntityBlock {
|
|
}
|
|
|
|
public static boolean isValidBookShelf(Level world, BlockPos tablePos, BlockPos bookshelfOffset) {
|
|
+ // Purpur Start
|
|
+ if(org.purpurmc.purpur.PurpurConfig.allowTransparentBlocksInEnchantmentBox){
|
|
+ return world.getBlockState(tablePos.offset(bookshelfOffset)).is(Blocks.BOOKSHELF) && !world.getBlockState(tablePos.offset(bookshelfOffset.getX() / 2, bookshelfOffset.getY(), bookshelfOffset.getZ() / 2)).isSuffocating(world, bookshelfOffset);
|
|
+ } // Purpur end
|
|
return world.getBlockState(tablePos.offset(bookshelfOffset)).is(Blocks.BOOKSHELF) && world.isEmptyBlock(tablePos.offset(bookshelfOffset.getX() / 2, bookshelfOffset.getY(), bookshelfOffset.getZ() / 2));
|
|
}
|
|
|
|
@@ -120,4 +126,18 @@ public class EnchantmentTableBlock extends BaseEntityBlock {
|
|
public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) {
|
|
return false;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean moved) {
|
|
+ BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
+
|
|
+ if (level.purpurConfig.enchantmentTableLapisPersists && blockEntity instanceof EnchantmentTableBlockEntity enchantmentTable) {
|
|
+ Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(Items.LAPIS_LAZULI, enchantmentTable.getLapis()));
|
|
+ level.updateNeighbourForOutputSignal(pos, this);
|
|
+ }
|
|
+
|
|
+ super.onRemove(state, level, pos, newState, moved);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java
|
|
index 41d7cff39fc37955877668337689b4b26cd8c7cf..2deddc746e43896584bd65ba8e7971a80acb4a4d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java
|
|
@@ -46,6 +46,14 @@ public class EndPortalBlock extends BaseEntityBlock {
|
|
public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
|
|
if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper
|
|
if (world instanceof ServerLevel && entity.canChangeDimensions() && Shapes.joinIsNotEmpty(Shapes.create(entity.getBoundingBox().move((double) (-pos.getX()), (double) (-pos.getY()), (double) (-pos.getZ()))), state.getShape(world, pos), BooleanOp.AND)) {
|
|
+ // Purpur start
|
|
+ if (entity.isPassenger() || entity.isVehicle()) {
|
|
+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_PORTAL).callEvent()) {
|
|
+ this.entityInside(state, world, pos, entity);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
ResourceKey<Level> resourcekey = world.getTypeKey() == LevelStem.END ? Level.OVERWORLD : Level.END; // CraftBukkit - SPIGOT-6152: send back to main overworld in custom ends
|
|
ServerLevel worldserver = ((ServerLevel) world).getServer().getLevel(resourcekey);
|
|
|
|
@@ -53,6 +61,22 @@ public class EndPortalBlock extends BaseEntityBlock {
|
|
// return; // CraftBukkit - always fire event in case plugins wish to change it
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (!world.purpurConfig.endPortalSafeTeleporting) {
|
|
+ // CraftBukkit start - Entity in portal
|
|
+ EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()));
|
|
+ world.getCraftServer().getPluginManager().callEvent(event);
|
|
+
|
|
+ if (entity instanceof ServerPlayer) {
|
|
+ ((ServerPlayer) entity).changeDimension(worldserver, PlayerTeleportEvent.TeleportCause.END_PORTAL);
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ entity.changeDimension(worldserver);
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// Paper start - move all of this logic into portal tick
|
|
entity.portalWorld = ((ServerLevel)world);
|
|
entity.portalBlock = pos.immutable();
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
|
|
index 7385e91f32f070e86a4e0fd3d214f55d832c7979..c3b78dd2d06be7d64920c6bcffcd16c82caa52b4 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
|
|
@@ -85,6 +85,27 @@ public class EnderChestBlock extends AbstractChestBlock<EnderChestBlockEntity> i
|
|
EnderChestBlockEntity enderChestBlockEntity = (EnderChestBlockEntity)blockEntity;
|
|
playerEnderChestContainer.setActiveChest(enderChestBlockEntity);
|
|
player.openMenu(new SimpleMenuProvider((syncId, inventory, playerx) -> {
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.enderChestSixRows) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) {
|
|
+ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkitPlayer = player.getBukkitEntity();
|
|
+ if (bukkitPlayer.hasPermission("purpur.enderchest.rows.six")) {
|
|
+ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.five")) {
|
|
+ return ChestMenu.fiveRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.four")) {
|
|
+ return ChestMenu.fourRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.three")) {
|
|
+ return ChestMenu.threeRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.two")) {
|
|
+ return ChestMenu.twoRows(syncId, inventory, playerEnderChestContainer);
|
|
+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.one")) {
|
|
+ return ChestMenu.oneRow(syncId, inventory, playerEnderChestContainer);
|
|
+ }
|
|
+ }
|
|
+ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer);
|
|
+ }
|
|
+ // Purpur end
|
|
return ChestMenu.threeRows(syncId, inventory, playerEnderChestContainer);
|
|
}, CONTAINER_TITLE));
|
|
player.awardStat(Stats.OPEN_ENDERCHEST);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
|
index 34d744837e599633a3c2c0b72f253bb0e157f226..69cc276fecd4cac51d38bd3cc7de490ad0ae8ace 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java
|
|
@@ -100,7 +100,7 @@ public class FarmBlock extends Block {
|
|
@Override
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
super.fallOn(world, state, pos, entity, fallDistance); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage.
|
|
- if (!world.isClientSide && world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) {
|
|
+ if (!world.isClientSide && (world.purpurConfig.farmlandTrampleHeight >= 0D ? fallDistance >= world.purpurConfig.farmlandTrampleHeight : world.random.nextFloat() < fallDistance - 0.5F) && entity instanceof LivingEntity && (entity instanceof Player || world.purpurConfig.farmlandBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur
|
|
// CraftBukkit start - Interact soil
|
|
org.bukkit.event.Cancellable cancellable;
|
|
if (entity instanceof Player) {
|
|
@@ -114,6 +114,22 @@ public class FarmBlock extends Block {
|
|
return;
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.farmlandTramplingDisabled) return;
|
|
+ if (world.purpurConfig.farmlandTramplingOnlyPlayers && !(entity instanceof Player)) return;
|
|
+ if (world.purpurConfig.farmlandAlpha) {
|
|
+ Block block = world.getBlockState(pos.below()).getBlock();
|
|
+ if (block instanceof FenceBlock || block instanceof WallBlock) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ if (world.purpurConfig.farmlandTramplingFeatherFalling) {
|
|
+ Iterator<net.minecraft.world.item.ItemStack> armor = entity.getArmorSlots().iterator();
|
|
+ if (armor.hasNext() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FALL_PROTECTION, armor.next()) >= (int) entity.fallDistance) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
if (CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
|
|
return;
|
|
}
|
|
@@ -163,7 +179,7 @@ public class FarmBlock extends Block {
|
|
}
|
|
}
|
|
|
|
- return false;
|
|
+ return ((ServerLevel) world).purpurConfig.farmlandGetsMoistFromBelow && world.getFluidState(pos.relative(Direction.DOWN)).is(FluidTags.WATER); // Purpur;
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
|
index 3a1aa4e2405090ccebefb7f5944f36462929e221..f3cf9f06de40054720d1847c1869a9d82592134d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
|
|
@@ -30,12 +30,12 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
|
|
@Override
|
|
public BlockState getStateForPlacement(LevelAccessor world) {
|
|
- return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, world.getRandom().nextInt(25));
|
|
+ return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, world.getRandom().nextInt(getMaxGrowthAge())); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public boolean isRandomlyTicking(BlockState state) {
|
|
- return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25;
|
|
+ return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < getMaxGrowthAge(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -51,7 +51,7 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
} else {
|
|
modifier = world.spigotConfig.caveVinesModifier;
|
|
}
|
|
- if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution
|
|
+ if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < getMaxGrowthAge() && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution // Purpur
|
|
// Spigot end
|
|
BlockPos blockposition1 = pos.relative(this.growthDirection);
|
|
|
|
@@ -73,11 +73,11 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
}
|
|
|
|
public BlockState getMaxAgeState(BlockState state) {
|
|
- return (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, 25);
|
|
+ return (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, getMaxGrowthAge()); // Purpur
|
|
}
|
|
|
|
public boolean isMaxAge(BlockState state) {
|
|
- return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) == 25;
|
|
+ return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) >= getMaxGrowthAge(); // Purpur
|
|
}
|
|
|
|
protected BlockState updateBodyAfterConvertedFromHead(BlockState from, BlockState to) {
|
|
@@ -119,13 +119,13 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
@Override
|
|
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
BlockPos blockposition1 = pos.relative(this.growthDirection);
|
|
- int i = Math.min((Integer) state.getValue(GrowingPlantHeadBlock.AGE) + 1, 25);
|
|
+ int i = Math.min((Integer) state.getValue(GrowingPlantHeadBlock.AGE) + 1, getMaxGrowthAge()); // Purpur
|
|
int j = this.getBlocksToGrowWhenBonemealed(random);
|
|
|
|
for (int k = 0; k < j && this.canGrowInto(world.getBlockState(blockposition1)); ++k) {
|
|
world.setBlockAndUpdate(blockposition1, (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, i));
|
|
blockposition1 = blockposition1.relative(this.growthDirection);
|
|
- i = Math.min(i + 1, 25);
|
|
+ i = Math.min(i + 1, getMaxGrowthAge()); // Purpur
|
|
}
|
|
|
|
}
|
|
@@ -138,4 +138,6 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
|
|
protected GrowingPlantHeadBlock getHeadBlock() {
|
|
return this;
|
|
}
|
|
+
|
|
+ public abstract int getMaxGrowthAge(); // Purpur
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/HayBlock.java b/src/main/java/net/minecraft/world/level/block/HayBlock.java
|
|
index cfbe1dae76db76cf54a4f5d72aca72d5e893859e..74cb10230d459ac9f300a9d59af504d233ac663e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/HayBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/HayBlock.java
|
|
@@ -15,6 +15,6 @@ public class HayBlock extends RotatedPillarBlock {
|
|
|
|
@Override
|
|
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
|
- entity.causeFallDamage(fallDistance, 0.2F, world.damageSources().fall());
|
|
+ super.fallOn(world, state, pos, entity, fallDistance); // Purpur
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/HugeMushroomBlock.java b/src/main/java/net/minecraft/world/level/block/HugeMushroomBlock.java
|
|
index 3c6d97b51c6fec130b80e5965afa2c49d48843c9..b456cb8efd8f0be8a6860c82462ce9bdde3a8383 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/HugeMushroomBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/HugeMushroomBlock.java
|
|
@@ -22,29 +22,65 @@ public class HugeMushroomBlock extends Block {
|
|
|
|
public HugeMushroomBlock(BlockBehaviour.Properties settings) {
|
|
super(settings);
|
|
- this.registerDefaultState(this.stateDefinition.any().setValue(NORTH, Boolean.valueOf(true)).setValue(EAST, Boolean.valueOf(true)).setValue(SOUTH, Boolean.valueOf(true)).setValue(WEST, Boolean.valueOf(true)).setValue(UP, Boolean.valueOf(true)).setValue(DOWN, Boolean.valueOf(true)));
|
|
+ // Purpur start
|
|
+ this.registerDefaultState(this.stateDefinition.any()
|
|
+ .setValue(NORTH, true)
|
|
+ .setValue(EAST, true)
|
|
+ .setValue(SOUTH, true)
|
|
+ .setValue(WEST, true)
|
|
+ .setValue(UP, true)
|
|
+ .setValue(DOWN, true));
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableMushroomBlockUpdates) return this.defaultBlockState(); // Purpur
|
|
BlockGetter blockGetter = ctx.getLevel();
|
|
BlockPos blockPos = ctx.getClickedPos();
|
|
- return this.defaultBlockState().setValue(DOWN, Boolean.valueOf(!blockGetter.getBlockState(blockPos.below()).is(this))).setValue(UP, Boolean.valueOf(!blockGetter.getBlockState(blockPos.above()).is(this))).setValue(NORTH, Boolean.valueOf(!blockGetter.getBlockState(blockPos.north()).is(this))).setValue(EAST, Boolean.valueOf(!blockGetter.getBlockState(blockPos.east()).is(this))).setValue(SOUTH, Boolean.valueOf(!blockGetter.getBlockState(blockPos.south()).is(this))).setValue(WEST, Boolean.valueOf(!blockGetter.getBlockState(blockPos.west()).is(this)));
|
|
+ // Purpur start
|
|
+ return this.defaultBlockState()
|
|
+ .setValue(DOWN, this != blockGetter.getBlockStateIfLoaded(blockPos.below()).getBlock())
|
|
+ .setValue(UP, this != blockGetter.getBlockStateIfLoaded(blockPos.above()).getBlock())
|
|
+ .setValue(NORTH, this != blockGetter.getBlockStateIfLoaded(blockPos.north()).getBlock())
|
|
+ .setValue(EAST, this != blockGetter.getBlockStateIfLoaded(blockPos.east()).getBlock())
|
|
+ .setValue(SOUTH, this != blockGetter.getBlockStateIfLoaded(blockPos.south()).getBlock())
|
|
+ .setValue(WEST, this != blockGetter.getBlockStateIfLoaded(blockPos.west()).getBlock());
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableMushroomBlockUpdates) return state; // Purpur
|
|
return neighborState.is(this) ? state.setValue(PROPERTY_BY_DIRECTION.get(direction), Boolean.valueOf(false)) : super.updateShape(state, direction, neighborState, world, pos, neighborPos);
|
|
}
|
|
|
|
@Override
|
|
public BlockState rotate(BlockState state, Rotation rotation) {
|
|
- return state.setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.NORTH)), state.getValue(NORTH)).setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.SOUTH)), state.getValue(SOUTH)).setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.EAST)), state.getValue(EAST)).setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.WEST)), state.getValue(WEST)).setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.UP)), state.getValue(UP)).setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.DOWN)), state.getValue(DOWN));
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableMushroomBlockUpdates) return state;
|
|
+ return state
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.NORTH)), state.getValue(NORTH))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.SOUTH)), state.getValue(SOUTH))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.EAST)), state.getValue(EAST))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.WEST)), state.getValue(NORTH))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.UP)), state.getValue(UP))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.DOWN)), state.getValue(DOWN));
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
public BlockState mirror(BlockState state, Mirror mirror) {
|
|
- return state.setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.NORTH)), state.getValue(NORTH)).setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.SOUTH)), state.getValue(SOUTH)).setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.EAST)), state.getValue(EAST)).setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.WEST)), state.getValue(WEST)).setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.UP)), state.getValue(UP)).setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.DOWN)), state.getValue(DOWN));
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableMushroomBlockUpdates) return state;
|
|
+ return state
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.NORTH)), state.getValue(NORTH))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.SOUTH)), state.getValue(SOUTH))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.EAST)), state.getValue(EAST))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.WEST)), state.getValue(NORTH))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.UP)), state.getValue(UP))
|
|
+ .setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.DOWN)), state.getValue(DOWN));
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java
|
|
index 5ecf02ce83b7496c977adfeb203b8eadb05f9da5..bf7f1ac5c691c0c4c30c124970f4b08a8108ad34 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/IceBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java
|
|
@@ -31,7 +31,7 @@ public class IceBlock extends HalfTransparentBlock {
|
|
public void afterDestroy(Level world, BlockPos pos, ItemStack tool) {
|
|
// Paper end
|
|
if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) == 0) {
|
|
- if (world.dimensionType().ultraWarm()) {
|
|
+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur
|
|
world.removeBlock(pos, false);
|
|
return;
|
|
}
|
|
@@ -59,7 +59,7 @@ public class IceBlock extends HalfTransparentBlock {
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
- if (world.dimensionType().ultraWarm()) {
|
|
+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur
|
|
world.removeBlock(pos, false);
|
|
} else {
|
|
world.setBlockAndUpdate(pos, Blocks.WATER.defaultBlockState());
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/KelpBlock.java b/src/main/java/net/minecraft/world/level/block/KelpBlock.java
|
|
index bc66fa91ec3e13431d5d9b6e17935cab73066be7..0f16b5ed2e249f3d8f583dc941e32066d354cf95 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/KelpBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/KelpBlock.java
|
|
@@ -64,4 +64,11 @@ public class KelpBlock extends GrowingPlantHeadBlock implements LiquidBlockConta
|
|
public FluidState getFluidState(BlockState state) {
|
|
return Fluids.WATER.getSource(false);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.kelpMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java
|
|
index 43e8ef1d6a65d4fd3fe53a587639ffb814368217..9c22a730772f71b34c63d1e43d48943f71e9990b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java
|
|
@@ -105,7 +105,7 @@ public class LiquidBlock extends Block implements BucketPickup {
|
|
|
|
@Override
|
|
public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
|
|
- if (this.shouldSpreadLiquid(world, pos, state)) {
|
|
+ if (world.purpurConfig.tickFluids && this.shouldSpreadLiquid(world, pos, state)) { // Purpur
|
|
world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper
|
|
}
|
|
|
|
@@ -129,7 +129,7 @@ public class LiquidBlock extends Block implements BucketPickup {
|
|
|
|
@Override
|
|
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
|
|
- if (state.getFluidState().isSource() || neighborState.getFluidState().isSource()) {
|
|
+ if (world.getMinecraftWorld().purpurConfig.tickFluids && state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { // Purpur
|
|
world.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(world));
|
|
}
|
|
|
|
@@ -138,7 +138,7 @@ public class LiquidBlock extends Block implements BucketPickup {
|
|
|
|
@Override
|
|
public void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
|
|
- if (this.shouldSpreadLiquid(world, pos, state)) {
|
|
+ if (world.purpurConfig.tickFluids && this.shouldSpreadLiquid(world, pos, state)) { // Purpur
|
|
world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/MagmaBlock.java b/src/main/java/net/minecraft/world/level/block/MagmaBlock.java
|
|
index 12ffb5714f088f4aeafa1ad6a36f5b64a86c4c96..293aa5c8f91a997045f8d9f2951fe3a7f01f0642 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/MagmaBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/MagmaBlock.java
|
|
@@ -27,7 +27,7 @@ public class MagmaBlock extends Block {
|
|
|
|
@Override
|
|
public void stepOn(Level world, BlockPos pos, BlockState state, Entity entity) {
|
|
- if (!entity.isSteppingCarefully() && entity instanceof LivingEntity && !EnchantmentHelper.hasFrostWalker((LivingEntity) entity)) {
|
|
+ if ((!entity.isSteppingCarefully() || world.purpurConfig.magmaBlockDamageWhenSneaking) && entity instanceof LivingEntity && (world.purpurConfig.magmaBlockDamageWithFrostWalker || !EnchantmentHelper.hasFrostWalker((LivingEntity) entity))) { // Purpur
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); // CraftBukkit
|
|
entity.hurt(world.damageSources().hotFloor(), 1.0F);
|
|
org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = null; // CraftBukkit
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
|
|
index a6ab0d0defc05e56a91084c49897059670a1324b..589b437e7c97c846410f293e2f014bdcd7cb333e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
|
|
@@ -52,7 +52,7 @@ public class NetherPortalBlock extends Block {
|
|
|
|
@Override
|
|
public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
|
|
- if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(2000) < world.getDifficulty().getId()) { // Spigot
|
|
+ if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(world.purpurConfig.piglinPortalSpawnModifier) < world.getDifficulty().getId()) { // Spigot // Purpur
|
|
while (world.getBlockState(pos).is((Block) this)) {
|
|
pos = pos.below();
|
|
}
|
|
@@ -84,6 +84,14 @@ public class NetherPortalBlock extends Block {
|
|
public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
|
|
if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper
|
|
if (entity.canChangeDimensions()) {
|
|
+ // Purpur start
|
|
+ if (entity.isPassenger() || entity.isVehicle()) {
|
|
+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.NETHER_PORTAL).callEvent()) {
|
|
+ this.entityInside(state, world, pos, entity);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start - Entity in portal
|
|
EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()));
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java
|
|
index e55720c4d2fbdf6aae526910e87a67c29cf906fd..bf4485b4cad324d5aace657ebf284c4d97197f53 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java
|
|
@@ -14,7 +14,7 @@ import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
-public class NetherWartBlock extends BushBlock {
|
|
+public class NetherWartBlock extends BushBlock implements BonemealableBlock { // Purpur
|
|
|
|
public static final int MAX_AGE = 3;
|
|
public static final IntegerProperty AGE = BlockStateProperties.AGE_3;
|
|
@@ -60,4 +60,32 @@ public class NetherWartBlock extends BushBlock {
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(NetherWartBlock.AGE);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void playerDestroy(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand) {
|
|
+ if (world.purpurConfig.hoeReplantsNetherWarts && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) {
|
|
+ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, Items.NETHER_WART);
|
|
+ } else {
|
|
+ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isValidBonemealTarget(net.minecraft.world.level.LevelReader world, BlockPos pos, BlockState state, boolean isClient) {
|
|
+ return ((net.minecraft.world.level.Level) world).purpurConfig.netherWartAffectedByBonemeal && state.getValue(NetherWartBlock.AGE) < 3;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ int i = Math.min(3, state.getValue(NetherWartBlock.AGE) + 1);
|
|
+ state = state.setValue(NetherWartBlock.AGE, i);
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, pos, state, 2); // CraftBukkit
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/NoteBlock.java b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
|
index a9c2d254bda5686a35ad2393534b85030dd8b136..c11752564ea48960232844ee735779aa95d82c12 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/NoteBlock.java
|
|
@@ -62,11 +62,13 @@ public class NoteBlock extends Block {
|
|
|
|
@Override
|
|
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableNoteBlockUpdates) return this.defaultBlockState(); // Purpur
|
|
return this.setInstrument(ctx.getLevel(), ctx.getClickedPos(), this.defaultBlockState());
|
|
}
|
|
|
|
@Override
|
|
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
|
|
+ if (org.purpurmc.purpur.PurpurConfig.disableNoteBlockUpdates) return state; // Purpur
|
|
boolean flag = NoteBlock.isFeatureFlagEnabled(world) ? direction.getAxis() == Direction.Axis.Y : direction == Direction.DOWN;
|
|
|
|
return flag ? this.setInstrument(world, pos, state) : super.updateShape(state, direction, neighborState, world, pos, neighborPos);
|
|
@@ -82,13 +84,14 @@ public class NoteBlock extends Block {
|
|
state = world.getBlockState(pos); // CraftBukkit - SPIGOT-5617: update in case changed in event
|
|
}
|
|
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.disableNoteBlockUpdates) // Purpur
|
|
world.setBlock(pos, (BlockState) state.setValue(NoteBlock.POWERED, flag1), 3);
|
|
}
|
|
|
|
}
|
|
|
|
private void playNote(@Nullable Entity entity, BlockState state, Level world, BlockPos pos) {
|
|
- if (!((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).requiresAirAbove() || world.getBlockState(pos.above()).isAir()) {
|
|
+ if (world.purpurConfig.noteBlockIgnoreAbove || !((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).requiresAirAbove() || world.getBlockState(pos.above()).isAir()) { // Purpur
|
|
// CraftBukkit start
|
|
// org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE));
|
|
// if (event.isCancelled()) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ObserverBlock.java b/src/main/java/net/minecraft/world/level/block/ObserverBlock.java
|
|
index 7b45d6b9a005036ca5051d089a7be792eb87012f..8806c97ecc6bdd8a64c2d82bb2f58f46ac37c468 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ObserverBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ObserverBlock.java
|
|
@@ -64,6 +64,7 @@ public class ObserverBlock extends DirectionalBlock {
|
|
@Override
|
|
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
|
|
if (state.getValue(ObserverBlock.FACING) == direction && !(Boolean) state.getValue(ObserverBlock.POWERED)) {
|
|
+ if (!world.getMinecraftWorld().purpurConfig.disableObserverClocks || !(neighborState.getBlock() instanceof ObserverBlock) || neighborState.getValue(ObserverBlock.FACING).getOpposite() != direction) // Purpur
|
|
this.startSignal(world, pos);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java
|
|
index 6b909d41ccdf6c1ac3ac0c4e673ff52f0d14a238..b8f69063cec4d31c9d9525a04c46ed8904ceff76 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java
|
|
@@ -188,7 +188,7 @@ public class PointedDripstoneBlock extends Block implements Fallable, SimpleWate
|
|
|
|
@VisibleForTesting
|
|
public static void maybeTransferFluid(BlockState state, ServerLevel world, BlockPos pos, float dripChance) {
|
|
- if (dripChance <= 0.17578125F || dripChance <= 0.05859375F) {
|
|
+ if (dripChance <= world.purpurConfig.cauldronDripstoneWaterFillChance || dripChance <= world.purpurConfig.cauldronDripstoneLavaFillChance) { // Purpur
|
|
if (PointedDripstoneBlock.isStalactiteStartPos(state, world, pos)) {
|
|
Optional<PointedDripstoneBlock.FluidInfo> optional = PointedDripstoneBlock.getFluidAboveStalactite(world, pos, state);
|
|
|
|
@@ -197,13 +197,13 @@ public class PointedDripstoneBlock extends Block implements Fallable, SimpleWate
|
|
float f1;
|
|
|
|
if (fluidtype == Fluids.WATER) {
|
|
- f1 = 0.17578125F;
|
|
+ f1 = world.purpurConfig.cauldronDripstoneWaterFillChance; // Purpur
|
|
} else {
|
|
if (fluidtype != Fluids.LAVA) {
|
|
return;
|
|
}
|
|
|
|
- f1 = 0.05859375F;
|
|
+ f1 = world.purpurConfig.cauldronDripstoneLavaFillChance; // Purpur
|
|
}
|
|
|
|
if (dripChance < f1) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java
|
|
index 518d3832c36c9ecf1ed9267ffc1f926dc84b7989..af5933b886abf3fd17bfdb8c1cb1ea63f6f2a757 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java
|
|
@@ -72,7 +72,7 @@ public class PowderSnowBlock extends Block implements BucketPickup {
|
|
if (!world.isClientSide) {
|
|
// CraftBukkit start
|
|
if (entity.isOnFire() && entity.mayInteract(world, pos)) {
|
|
- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !(world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || entity instanceof Player)).isCancelled()) {
|
|
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !((world.purpurConfig.powderSnowBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) || entity instanceof Player)).isCancelled()) {
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
|
|
index 7fddb6fa8fd30ef88346a59f7867aae792f13772..40893e71fe8447b695350273bef9623bd5accdcd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
|
|
@@ -23,7 +23,7 @@ public class PoweredRailBlock extends BaseRailBlock {
|
|
}
|
|
|
|
protected boolean findPoweredRailSignal(Level world, BlockPos pos, BlockState state, boolean flag, int distance) {
|
|
- if (distance >= 8) {
|
|
+ if (distance >= world.purpurConfig.railActivationRange) { // Purpur
|
|
return false;
|
|
} else {
|
|
int j = pos.getX();
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java
|
|
index 2ed78cf83c0ae66a6ddba1ff307da89a24b0d0a8..ae17d6a54fad0bd2d71d306f418b5ced2f11b863 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java
|
|
@@ -141,7 +141,7 @@ public class RespawnAnchorBlock extends Block {
|
|
};
|
|
Vec3 vec3d = explodedPos.getCenter();
|
|
|
|
- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), explosiondamagecalculator, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // Paper
|
|
+ if (world.purpurConfig.respawnAnchorExplode)world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), explosiondamagecalculator, vec3d, (float) world.purpurConfig.respawnAnchorExplosionPower, world.purpurConfig.respawnAnchorExplosionFire, world.purpurConfig.respawnAnchorExplosionEffect); // Paper // Purpur
|
|
}
|
|
|
|
public static boolean canSetSpawn(Level world) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java b/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java
|
|
index 437b44fb68bcbe81d1c431689431225b6a17a1a6..06d091b7c4df949c4abda16c4f73c194a71a4669 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java
|
|
@@ -130,7 +130,7 @@ public class SculkShriekerBlock extends BaseEntityBlock implements SimpleWaterlo
|
|
@Nullable
|
|
@Override
|
|
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
|
|
- return (BlockState) this.defaultBlockState().setValue(SculkShriekerBlock.WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).getType() == Fluids.WATER);
|
|
+ return (BlockState) this.defaultBlockState().setValue(SculkShriekerBlock.WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).getType() == Fluids.WATER).setValue(SculkShriekerBlock.CAN_SUMMON, ctx.getLevel().purpurConfig.sculkShriekerCanSummonDefault); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
|
index c89978ecbc5a13dda6f76ea6d1cc3056efc9a174..39868ad3ee4bb573a4dd562894d93f64be4ee5ac 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
|
@@ -138,7 +138,7 @@ public class ShulkerBoxBlock extends BaseEntityBlock {
|
|
public void playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) {
|
|
BlockEntity blockEntity = world.getBlockEntity(pos);
|
|
if (blockEntity instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) {
|
|
- if (!world.isClientSide && player.isCreative() && !shulkerBoxBlockEntity.isEmpty()) {
|
|
+ if (world.purpurConfig.shulkerBoxAllowOversizedStacks || (!world.isClientSide && player.isCreative() && !shulkerBoxBlockEntity.isEmpty())) { // Purpur
|
|
ItemStack itemStack = getColoredItemStack(this.getColor());
|
|
blockEntity.saveToItem(itemStack);
|
|
if (shulkerBoxBlockEntity.hasCustomName()) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SignBlock.java b/src/main/java/net/minecraft/world/level/block/SignBlock.java
|
|
index aface9a9697095a29edaf73c9cdabc2c1414b9d7..1a04d0a601b8e481dd6e2592b849b907a5b9f63f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SignBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SignBlock.java
|
|
@@ -14,6 +14,7 @@ import net.minecraft.world.item.DyeItem;
|
|
import net.minecraft.world.item.Item;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
+import net.minecraft.world.item.SignItem;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
@@ -76,11 +77,11 @@ public abstract class SignBlock extends BaseEntityBlock implements SimpleWaterlo
|
|
if (world.isClientSide) {
|
|
return bl4 ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
|
|
} else {
|
|
- BlockEntity bl5 = world.getBlockEntity(pos);
|
|
- if (!(bl5 instanceof SignBlockEntity)) {
|
|
+ BlockEntity blockEntity = world.getBlockEntity(pos); // Purpur - decompile fix
|
|
+ if (!(blockEntity instanceof SignBlockEntity)) { // Purpur - decompile fix
|
|
return InteractionResult.PASS;
|
|
} else {
|
|
- SignBlockEntity signBlockEntity = (SignBlockEntity)bl5;
|
|
+ SignBlockEntity signBlockEntity = (SignBlockEntity)blockEntity; // Purpur - decompile fix
|
|
boolean bl5 = signBlockEntity.hasGlowingText();
|
|
if ((!bl2 || !bl5) && (!bl3 || bl5)) {
|
|
if (bl4) {
|
|
@@ -108,6 +109,17 @@ public abstract class SignBlock extends BaseEntityBlock implements SimpleWaterlo
|
|
}
|
|
}
|
|
|
|
+ // Purpur start - right click to open sign editor
|
|
+ if (world.purpurConfig.signRightClickEdit && itemStack.getItem() instanceof SignItem &&
|
|
+ !player.isCrouching() && player.getAbilities().mayBuild &&
|
|
+ player.getBukkitEntity().hasPermission("purpur.sign.edit")) {
|
|
+ signBlockEntity.setEditable(true);
|
|
+ signBlockEntity.setAllowedPlayerEditor(player.getUUID());
|
|
+ player.openTextEdit(signBlockEntity);
|
|
+ return InteractionResult.SUCCESS;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return signBlockEntity.executeClickCommands((ServerPlayer)player) ? InteractionResult.SUCCESS : InteractionResult.PASS;
|
|
} else {
|
|
return InteractionResult.PASS;
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SlabBlock.java b/src/main/java/net/minecraft/world/level/block/SlabBlock.java
|
|
index 18b603d646081926343dea108b55d641df1c2c34..03ad3e45fc6d48091ac0c0ba5dc3d014b1d4ddfa 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SlabBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SlabBlock.java
|
|
@@ -130,4 +130,25 @@ public class SlabBlock extends Block implements SimpleWaterloggedBlock {
|
|
return false;
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public boolean halfBreak(BlockState state, BlockPos pos, net.minecraft.server.level.ServerPlayer player) {
|
|
+ if (state.getValue(SlabBlock.TYPE) != SlabType.DOUBLE) {
|
|
+ return false;
|
|
+ }
|
|
+ net.minecraft.world.phys.HitResult result = player.getRayTrace(16, net.minecraft.world.level.ClipContext.Fluid.NONE);
|
|
+ if (result.getType() != net.minecraft.world.phys.HitResult.Type.BLOCK) {
|
|
+ return false;
|
|
+ }
|
|
+ double hitY = result.getLocation().y();
|
|
+ int blockY = org.bukkit.util.NumberConversions.floor(hitY);
|
|
+ player.level.setBlock(pos, state.setValue(SlabBlock.TYPE, (hitY - blockY > 0.5 || blockY - pos.getY() == 1) ? SlabType.BOTTOM : SlabType.TOP), 3);
|
|
+ if (!player.getAbilities().instabuild) {
|
|
+ net.minecraft.world.entity.item.ItemEntity item = new net.minecraft.world.entity.item.ItemEntity(player.level, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(asItem()));
|
|
+ item.setDefaultPickUpDelay();
|
|
+ player.level.addFreshEntity(item);
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java b/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java
|
|
index 14e00c7feb1c051d56a3d27cd00dcef072dd771a..4952fb1aaaafb55baa0fddb389f966a120a4786c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java
|
|
@@ -81,6 +81,12 @@ public class SnowLayerBlock extends Block {
|
|
public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) {
|
|
BlockState iblockdata1 = world.getBlockState(pos.below());
|
|
|
|
+ // Purpur start
|
|
+ if (iblockdata1.is(Blocks.BLUE_ICE) && !world.getWorldBorder().world.purpurConfig.snowOnBlueIce) {
|
|
+ return false;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return iblockdata1.is(BlockTags.SNOW_LAYER_CANNOT_SURVIVE_ON) ? false : (iblockdata1.is(BlockTags.SNOW_LAYER_CAN_SURVIVE_ON) ? true : Block.isFaceFull(iblockdata1.getCollisionShape(world, pos.below()), Direction.UP) || iblockdata1.is((Block) this) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 8);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java b/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java
|
|
index 936d844a5a246138c9f9ae4ae6e318242b8f1420..d58dc4aa02fe371deaf879df8692dbe93c648f9b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java
|
|
@@ -40,6 +40,58 @@ public class SpawnerBlock extends BaseEntityBlock {
|
|
return createTickerHelper(type, BlockEntityType.MOB_SPAWNER, world.isClientSide ? SpawnerBlockEntity::clientTick : SpawnerBlockEntity::serverTick);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void playerDestroy(Level level, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, ItemStack stack) {
|
|
+ if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.drop.spawners") && isSilkTouch(level, stack)) {
|
|
+ Optional<net.minecraft.world.entity.EntityType<?>> type = net.minecraft.world.entity.EntityType.by(((SpawnerBlockEntity) blockEntity).getSpawner().nextSpawnData.getEntityToSpawn());
|
|
+
|
|
+ net.minecraft.world.entity.EntityType<?> entityType = type.orElse(null);
|
|
+ final net.kyori.adventure.text.Component mobName = io.papermc.paper.adventure.PaperAdventure.asAdventure(entityType == null ? Component.empty() : entityType.getDescription());
|
|
+ CompoundTag display = new CompoundTag();
|
|
+ CompoundTag tag = new CompoundTag();
|
|
+
|
|
+ String name = level.purpurConfig.silkTouchSpawnerName;
|
|
+ if (name != null && !name.isEmpty() && !name.equals("Monster Spawner")) {
|
|
+ net.kyori.adventure.text.Component displayName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName));
|
|
+ if (name.startsWith("<reset>")) {
|
|
+ displayName = displayName.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false);
|
|
+ }
|
|
+ display.put("Name", net.minecraft.nbt.StringTag.valueOf(io.papermc.paper.adventure.PaperAdventure.asJsonString(displayName, java.util.Locale.ROOT)));
|
|
+ tag.put("display", display);
|
|
+ }
|
|
+
|
|
+ List<String> lore = level.purpurConfig.silkTouchSpawnerLore;
|
|
+ if (lore != null && !lore.isEmpty()) {
|
|
+ net.minecraft.nbt.ListTag list = new net.minecraft.nbt.ListTag();
|
|
+ for (String line : lore) {
|
|
+ net.kyori.adventure.text.Component lineComponent = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(line, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName));
|
|
+ if (line.startsWith("<reset>")) {
|
|
+ lineComponent = lineComponent.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false);
|
|
+ }
|
|
+ list.add(net.minecraft.nbt.StringTag.valueOf(io.papermc.paper.adventure.PaperAdventure.asJsonString(lineComponent, java.util.Locale.ROOT)));
|
|
+ }
|
|
+ display.put("Lore", list);
|
|
+ tag.put("display", display);
|
|
+ }
|
|
+
|
|
+ ItemStack item = new ItemStack(Blocks.SPAWNER.asItem());
|
|
+ if (entityType != null) {
|
|
+ tag.putString("Purpur.mob_type", entityType.getName());
|
|
+ tag.putDouble("HideFlags", 32); // hides the "Interact with Spawn Egg" tooltip
|
|
+ item.setTag(tag);
|
|
+ }
|
|
+
|
|
+ popResource(level, pos, item);
|
|
+ }
|
|
+ super.playerDestroy(level, player, pos, state, blockEntity, stack);
|
|
+ }
|
|
+
|
|
+ private boolean isSilkTouch(Level level, ItemStack stack) {
|
|
+ return stack != null && level.purpurConfig.silkTouchTools.contains(stack.getItem()) && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.SILK_TOUCH, stack) >= level.purpurConfig.minimumSilkTouchSpawnerRequire;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void spawnAfterBreak(BlockState state, ServerLevel world, BlockPos pos, ItemStack tool, boolean dropExperience) {
|
|
super.spawnAfterBreak(state, world, pos, tool, dropExperience);
|
|
@@ -48,6 +100,7 @@ public class SpawnerBlock extends BaseEntityBlock {
|
|
|
|
@Override
|
|
public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) {
|
|
+ if (isSilkTouch(worldserver, itemstack)) return 0; // Purpur
|
|
if (flag) {
|
|
int i = 15 + worldserver.random.nextInt(15) + worldserver.random.nextInt(15);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
|
|
index 7304b2659eb45bc4bc9fa7c43e6ca07221d0fc73..df04a571ebd3c04bc7b58c1ee5661a1f03c69d2f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
|
|
@@ -73,16 +73,16 @@ public class SpongeBlock extends Block {
|
|
// CraftBukkit end
|
|
Material material = iblockdata.getMaterial();
|
|
|
|
- if (fluid.is(FluidTags.WATER)) {
|
|
+ if (fluid.is(FluidTags.WATER) || (world.purpurConfig.spongeAbsorbsLava && fluid.is(FluidTags.LAVA))) { // Purpur
|
|
if (iblockdata.getBlock() instanceof BucketPickup && !((BucketPickup) iblockdata.getBlock()).pickupBlock(blockList, blockposition2, iblockdata).isEmpty()) { // CraftBukkit
|
|
++i;
|
|
- if (j < 6) {
|
|
+ if (j < world.purpurConfig.spongeAbsorptionRadius) { // Purpur
|
|
queue.add(new Tuple<>(blockposition2, j + 1));
|
|
}
|
|
} else if (iblockdata.getBlock() instanceof LiquidBlock) {
|
|
blockList.setBlock(blockposition2, Blocks.AIR.defaultBlockState(), 3); // CraftBukkit
|
|
++i;
|
|
- if (j < 6) {
|
|
+ if (j < world.purpurConfig.spongeAbsorptionRadius) { // Purpur
|
|
queue.add(new Tuple<>(blockposition2, j + 1));
|
|
}
|
|
} else if (material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT) {
|
|
@@ -93,14 +93,14 @@ public class SpongeBlock extends Block {
|
|
blockList.setBlock(blockposition2, Blocks.AIR.defaultBlockState(), 3);
|
|
// CraftBukkit end
|
|
++i;
|
|
- if (j < 6) {
|
|
+ if (j < world.purpurConfig.spongeAbsorptionRadius) { // Purpur
|
|
queue.add(new Tuple<>(blockposition2, j + 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- if (i > 64) {
|
|
+ if (i > world.purpurConfig.spongeAbsorptionArea) { // Purpur
|
|
break;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
|
|
index 0a95842c53a9d0286c57bcb42db97e468e30fb7d..e2d42e7947a237dd060ec1b9b63ac6ca4f37241a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
|
|
@@ -92,4 +92,16 @@ public class StonecutterBlock extends Block {
|
|
public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) {
|
|
return false;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void stepOn(Level level, BlockPos pos, BlockState state, net.minecraft.world.entity.Entity entity) {
|
|
+ if (level.purpurConfig.stonecutterDamage > 0.0F && entity instanceof net.minecraft.world.entity.LivingEntity) {
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = level.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
|
|
+ entity.hurt(entity.damageSources().magic(), level.purpurConfig.stonecutterDamage);
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = null;
|
|
+ }
|
|
+ super.stepOn(level, pos, state, entity);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
|
|
index 6b400a4759c8c8612a3b5c96ca0d87ef9dc71435..992de1ab2c00a2545a857f1b5533926bc895f996 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
|
|
@@ -19,7 +19,7 @@ import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
-public class SugarCaneBlock extends Block {
|
|
+public class SugarCaneBlock extends Block implements BonemealableBlock { // Purpur
|
|
|
|
public static final IntegerProperty AGE = BlockStateProperties.AGE_15;
|
|
protected static final float AABB_OFFSET = 6.0F;
|
|
@@ -106,4 +106,34 @@ public class SugarCaneBlock extends Block {
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(SugarCaneBlock.AGE);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isValidBonemealTarget(LevelReader world, BlockPos pos, BlockState state, boolean isClient) {
|
|
+ if (!((net.minecraft.world.level.Level) world).purpurConfig.sugarCanAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false;
|
|
+
|
|
+ int reedHeight = 0;
|
|
+ while (world.getBlockState(pos.below(reedHeight)).is(this)) {
|
|
+ reedHeight++;
|
|
+ }
|
|
+
|
|
+ return reedHeight < ((net.minecraft.world.level.Level) world).paperConfig().maxGrowthHeight.reeds;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
|
|
+ int reedHeight = 0;
|
|
+ while (world.getBlockState(pos.below(reedHeight)).is(this)) {
|
|
+ reedHeight++;
|
|
+ }
|
|
+ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.reeds - reedHeight; i++) {
|
|
+ world.setBlockAndUpdate(pos.above(i), state.setValue(SugarCaneBlock.AGE, 0));
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java
|
|
index 6c1a0e6f961e46a1a89850746a71e97b32514adf..a8c227e2cb62cfa8225798329cde9078d194c776 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java
|
|
@@ -160,7 +160,7 @@ public class TurtleEggBlock extends Block {
|
|
private boolean shouldUpdateHatchLevel(Level world) {
|
|
float f = world.getTimeOfDay(1.0F);
|
|
|
|
- return (double) f < 0.69D && (double) f > 0.65D ? true : world.random.nextInt(500) == 0;
|
|
+ return (double) f < 0.69D && (double) f > 0.65D ? true : world.random.nextInt(world.purpurConfig.turtleEggsRandomTickCrackChance) == 0;
|
|
}
|
|
|
|
@Override
|
|
@@ -193,6 +193,31 @@ public class TurtleEggBlock extends Block {
|
|
}
|
|
|
|
private boolean canDestroyEgg(Level world, Entity entity) {
|
|
- return !(entity instanceof Turtle) && !(entity instanceof Bat) ? (!(entity instanceof LivingEntity) ? false : entity instanceof Player || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) : false;
|
|
+ // Purpur start
|
|
+ if (entity instanceof Turtle || entity instanceof Bat) {
|
|
+ return false;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsBreakFromExpOrbs && entity instanceof net.minecraft.world.entity.ExperienceOrb) {
|
|
+ return true;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsBreakFromItems && entity instanceof net.minecraft.world.entity.item.ItemEntity) {
|
|
+ return true;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsBreakFromMinecarts && entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) {
|
|
+ return true;
|
|
+ }
|
|
+ if (!(entity instanceof LivingEntity)) {
|
|
+ return false;
|
|
+ }
|
|
+ if (world.purpurConfig.turtleEggsTramplingFeatherFalling) {
|
|
+ java.util.Iterator<ItemStack> armor = entity.getArmorSlots().iterator();
|
|
+ return !armor.hasNext() || net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FALL_PROTECTION, armor.next()) < (int) entity.fallDistance;
|
|
+ }
|
|
+ if (entity instanceof Player) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return world.purpurConfig.turtleEggsBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java b/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java
|
|
index 6866605c7ef5361b21130a19a59c3fa3660dfb19..dee5d76d29da13f8639ab5d392cd0143201e71ba 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java
|
|
@@ -27,4 +27,11 @@ public class TwistingVinesBlock extends GrowingPlantHeadBlock {
|
|
protected boolean canGrowInto(BlockState state) {
|
|
return NetherVines.isValidGrowthState(state);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.twistingVinesMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java b/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java
|
|
index e5c135ec059746b75fe58516809584221285cdbe..713c7e6e31a3e1097b612c77a4fce147c9252e0b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java
|
|
@@ -27,4 +27,11 @@ public class WeepingVinesBlock extends GrowingPlantHeadBlock {
|
|
protected boolean canGrowInto(BlockState state) {
|
|
return NetherVines.isValidGrowthState(state);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public int getMaxGrowthAge() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.weepingVinesMaxGrowthAge;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java b/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java
|
|
index b91effe91dad2e1aeea0ea31140f7432833b343f..bb628bd3fe8b185f356968697b17e1c4a442a6d2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java
|
|
@@ -71,6 +71,7 @@ public class WitherSkullBlock extends SkullBlock {
|
|
entitywither.moveTo((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.55D, (double) blockposition1.getZ() + 0.5D, shapedetector_shapedetectorcollection.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F, 0.0F);
|
|
entitywither.yBodyRot = shapedetector_shapedetectorcollection.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F;
|
|
entitywither.makeInvulnerable();
|
|
+ entitywither.setSummoner(iblockdata.getBlock().placer == null ? null : iblockdata.getBlock().placer.getUUID()); // Purpur
|
|
// CraftBukkit start
|
|
if (!world.addFreshEntity(entitywither, SpawnReason.BUILD_WITHER)) {
|
|
return;
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
|
index a3f073066f6e2eea8964461ad2b0409ade202f35..5cd7b4e7065070bf9fcc34b621dba2ccba99a573 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
|
@@ -44,6 +44,7 @@ import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.AbstractFurnaceBlock;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.phys.Vec3;
|
|
// CraftBukkit start
|
|
import org.bukkit.craftbukkit.block.CraftBlock;
|
|
@@ -207,6 +208,22 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
|
|
// Paper end
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public static void addFuel(ItemStack itemStack, Integer burnTime) {
|
|
+ Map<Item, Integer> map = Maps.newLinkedHashMap();
|
|
+ map.putAll(getFuel());
|
|
+ map.put(itemStack.getItem(), burnTime);
|
|
+ cachedBurnDurations = com.google.common.collect.ImmutableMap.copyOf(map);
|
|
+ }
|
|
+
|
|
+ public static void removeFuel(ItemStack itemStack) {
|
|
+ Map<Item, Integer> map = Maps.newLinkedHashMap();
|
|
+ map.putAll(getFuel());
|
|
+ map.remove(itemStack.getItem());
|
|
+ cachedBurnDurations = com.google.common.collect.ImmutableMap.copyOf(map);
|
|
+ }
|
|
+ // Purpur End
|
|
+
|
|
// CraftBukkit start - add fields and methods
|
|
private int maxStack = MAX_STACK;
|
|
public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
|
|
@@ -324,6 +341,21 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
|
|
}
|
|
|
|
ItemStack itemstack = (ItemStack) blockEntity.items.get(1);
|
|
+ // Purpur start
|
|
+ boolean usedLavaFromUnderneath = false;
|
|
+ if (world.purpurConfig.furnaceUseLavaFromUnderneath && !blockEntity.isLit() && itemstack.isEmpty() && !blockEntity.items.get(0).isEmpty() && world.getGameTime() % 20 == 0) {
|
|
+ BlockPos below = blockEntity.getBlockPos().below();
|
|
+ BlockState belowState = world.getBlockStateIfLoaded(below);
|
|
+ if (belowState != null && belowState.is(Blocks.LAVA)) {
|
|
+ FluidState fluidState = belowState.getFluidState();
|
|
+ if (fluidState != null && fluidState.isSource()) {
|
|
+ world.setBlock(below, Blocks.AIR.defaultBlockState(), 3);
|
|
+ itemstack = Items.LAVA_BUCKET.getDefaultInstance();
|
|
+ usedLavaFromUnderneath = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
boolean flag2 = !((ItemStack) blockEntity.items.get(0)).isEmpty();
|
|
boolean flag3 = !itemstack.isEmpty();
|
|
|
|
@@ -409,6 +441,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
|
|
setChanged(world, pos, state);
|
|
}
|
|
|
|
+ if (usedLavaFromUnderneath) blockEntity.items.set(1, ItemStack.EMPTY); // Purpur
|
|
}
|
|
|
|
private static boolean canBurn(RegistryAccess registryManager, @Nullable Recipe<?> recipe, NonNullList<ItemStack> slots, int count) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
|
index 416aa989ebb18a8741cc9d605a1180ab830f6643..e38a0adf5463c48311ad08b8d2e5b5c2d989a3b5 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
|
@@ -67,7 +67,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
|
|
|
public BarrelBlockEntity(BlockPos pos, BlockState state) {
|
|
super(BlockEntityType.BARREL, pos, state);
|
|
- this.items = NonNullList.withSize(27, ItemStack.EMPTY);
|
|
+ // Purpur start
|
|
+ this.items = NonNullList.withSize(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> 54;
|
|
+ case 5 -> 45;
|
|
+ case 4 -> 36;
|
|
+ case 2 -> 18;
|
|
+ case 1 -> 9;
|
|
+ default -> 27;
|
|
+ }, ItemStack.EMPTY);
|
|
+ // Purpur end
|
|
this.openersCounter = new ContainerOpenersCounter() {
|
|
@Override
|
|
protected void onOpen(Level world, BlockPos pos, BlockState state) {
|
|
@@ -118,7 +127,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
|
|
|
@Override
|
|
public int getContainerSize() {
|
|
- return 27;
|
|
+ // Purpur start
|
|
+ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> 54;
|
|
+ case 5 -> 45;
|
|
+ case 4 -> 36;
|
|
+ case 2 -> 18;
|
|
+ case 1 -> 9;
|
|
+ default -> 27;
|
|
+ };
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -138,7 +156,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
|
|
|
@Override
|
|
protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) {
|
|
- return ChestMenu.threeRows(syncId, playerInventory, this);
|
|
+ // Purpur start
|
|
+ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> ChestMenu.sixRows(syncId, playerInventory, this);
|
|
+ case 5 -> ChestMenu.fiveRows(syncId, playerInventory, this);
|
|
+ case 4 -> ChestMenu.fourRows(syncId, playerInventory, this);
|
|
+ case 2 -> ChestMenu.twoRows(syncId, playerInventory, this);
|
|
+ case 1 -> ChestMenu.oneRow(syncId, playerInventory, this);
|
|
+ default -> ChestMenu.threeRows(syncId, playerInventory, this);
|
|
+ };
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java
|
|
index ef740d1ad6352ca4af299001a081b720bc472d2e..8f82b0ce87afc8890c5b3386d5f6e22c48974b16 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java
|
|
@@ -84,6 +84,16 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
|
|
public double getEffectRange() {
|
|
if (this.effectRange < 0) {
|
|
+ // Purpur Start
|
|
+ if (this.level != null) {
|
|
+ switch (this.levels) {
|
|
+ case 1: return this.level.purpurConfig.beaconLevelOne;
|
|
+ case 2: return this.level.purpurConfig.beaconLevelTwo;
|
|
+ case 3: return this.level.purpurConfig.beaconLevelThree;
|
|
+ case 4: return this.level.purpurConfig.beaconLevelFour;
|
|
+ }
|
|
+ }
|
|
+ // Purpur End
|
|
return this.levels * 10 + 10;
|
|
} else {
|
|
return effectRange;
|
|
@@ -155,6 +165,7 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
int j = pos.getY();
|
|
int k = pos.getZ();
|
|
BlockPos blockposition1;
|
|
+ boolean isTintedGlass = false;
|
|
|
|
if (blockEntity.lastCheckY < j) {
|
|
blockposition1 = pos;
|
|
@@ -188,6 +199,9 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
}
|
|
}
|
|
} else {
|
|
+ if (world.purpurConfig.beaconAllowEffectsWithTintedGlass && block.equals(Blocks.TINTED_GLASS)) {
|
|
+ isTintedGlass = true;
|
|
+ }
|
|
if (tileentitybeacon_beaconcolortracker == null || iblockdata1.getLightBlock(world, blockposition1) >= 15 && !iblockdata1.is(Blocks.BEDROCK)) {
|
|
blockEntity.checkingBeamSections.clear();
|
|
blockEntity.lastCheckY = l;
|
|
@@ -207,7 +221,7 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name
|
|
blockEntity.levels = BeaconBlockEntity.updateBase(world, i, j, k);
|
|
}
|
|
|
|
- if (blockEntity.levels > 0 && !blockEntity.beamSections.isEmpty()) {
|
|
+ if (blockEntity.levels > 0 && (!blockEntity.beamSections.isEmpty() || (world.purpurConfig.beaconAllowEffectsWithTintedGlass && isTintedGlass))) {
|
|
BeaconBlockEntity.applyEffects(world, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower, blockEntity); // Paper
|
|
BeaconBlockEntity.playSound(world, pos, SoundEvents.BEACON_AMBIENT);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
|
index 41c9f074203915c31c1ae7a160ce509c13383f84..7b82842b97ce795745cf6ee6399f618c55acbbf3 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java
|
|
@@ -43,7 +43,7 @@ public class BeehiveBlockEntity extends BlockEntity {
|
|
private final List<BeehiveBlockEntity.BeeData> stored = Lists.newArrayList();
|
|
@Nullable
|
|
public BlockPos savedFlowerPos;
|
|
- public int maxBees = 3; // CraftBukkit - allow setting max amount of bees a hive can hold
|
|
+ public int maxBees = org.purpurmc.purpur.PurpurConfig.beeInsideBeeHive; // CraftBukkit - allow setting max amount of bees a hive can hold // Purpur
|
|
|
|
public BeehiveBlockEntity(BlockPos pos, BlockState state) {
|
|
super(BlockEntityType.BEEHIVE, pos, state);
|
|
@@ -203,7 +203,7 @@ public class BeehiveBlockEntity extends BlockEntity {
|
|
}
|
|
|
|
private static boolean releaseBee(Level world, BlockPos blockposition, BlockState iblockdata, BeehiveBlockEntity.BeeData tileentitybeehive_hivebee, @Nullable List<Entity> list, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, @Nullable BlockPos blockposition1, boolean force) {
|
|
- if (!force && (world.isNight() || world.isRaining()) && tileentitybeehive_releasestatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) {
|
|
+ if (!force && ((world.isNight() && !world.purpurConfig.beeCanWorkAtNight) || (world.isRaining() && !world.purpurConfig.beeCanWorkInRain)) && tileentitybeehive_releasestatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { // Purpur
|
|
// CraftBukkit end
|
|
return false;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
index 1b248db497500aa6bd346b306dcb908af77626f3..e438e7e018f643d82ddf5efbf72779876c516d1a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
@@ -6,6 +6,8 @@ import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.nbt.ListTag;
|
|
+import net.minecraft.nbt.StringTag;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
@@ -74,10 +76,27 @@ public abstract class BlockEntity {
|
|
if (persistentDataTag instanceof CompoundTag) {
|
|
this.persistentDataContainer.putAll((CompoundTag) persistentDataTag);
|
|
}
|
|
+ // Purpur start
|
|
+ if (nbt.contains("Purpur.persistentDisplayName")) {
|
|
+ this.persistentDisplayName = nbt.getString("Purpur.persistentDisplayName");
|
|
+ }
|
|
+ if (nbt.contains("Purpur.persistentLore")) {
|
|
+ this.persistentLore = nbt.getList("Purpur.persistentLore", 8);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
// CraftBukkit end
|
|
|
|
- protected void saveAdditional(CompoundTag nbt) {}
|
|
+ protected void saveAdditional(CompoundTag nbt) {
|
|
+ // Purpur start
|
|
+ if (this.persistentDisplayName != null) {
|
|
+ nbt.put("Purpur.persistentDisplayName", StringTag.valueOf(this.persistentDisplayName));
|
|
+ }
|
|
+ if (this.persistentLore != null) {
|
|
+ nbt.put("Purpur.persistentLore", this.persistentLore);
|
|
+ }
|
|
+ // Purpur end
|
|
+ }
|
|
|
|
public final CompoundTag saveWithFullMetadata() {
|
|
CompoundTag nbttagcompound = this.saveWithoutMetadata();
|
|
@@ -187,10 +206,24 @@ public abstract class BlockEntity {
|
|
|
|
@Nullable
|
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
|
+ // Purpur start
|
|
+ if (this instanceof net.minecraft.world.Nameable nameable && nameable.hasCustomName()) {
|
|
+ CompoundTag nbt = this.saveWithoutMetadata();
|
|
+ nbt.remove("Items");
|
|
+ return net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket.create(this, $ -> nbt);
|
|
+ }
|
|
+ // Purpur end
|
|
return null;
|
|
}
|
|
|
|
public CompoundTag getUpdateTag() {
|
|
+ // Purpur start
|
|
+ if (this instanceof net.minecraft.world.Nameable nameable && nameable.hasCustomName()) {
|
|
+ CompoundTag nbt = this.saveWithoutMetadata();
|
|
+ nbt.remove("Items");
|
|
+ return nbt;
|
|
+ }
|
|
+ // Purpur end
|
|
return new CompoundTag();
|
|
}
|
|
|
|
@@ -264,4 +297,24 @@ public abstract class BlockEntity {
|
|
}
|
|
// Paper end
|
|
|
|
+ // Purpur start
|
|
+ private String persistentDisplayName = null;
|
|
+ private ListTag persistentLore = null;
|
|
+
|
|
+ public void setPersistentDisplayName(String json) {
|
|
+ this.persistentDisplayName = json;
|
|
+ }
|
|
+
|
|
+ public void setPersistentLore(ListTag lore) {
|
|
+ this.persistentLore = lore;
|
|
+ }
|
|
+
|
|
+ public String getPersistentDisplayName() {
|
|
+ return this.persistentDisplayName;
|
|
+ }
|
|
+
|
|
+ public ListTag getPersistentLore() {
|
|
+ return this.persistentLore;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java
|
|
index 963a596154091b79ca139af6274aa323518ad1ad..4dcac3899a500d8586580bcfd5b4516e1dcdcd4a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java
|
|
@@ -171,7 +171,7 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
if ((l > 1 || i1 > 1 || j1 > 1) && (i == 0 && (i1 == 2 || j1 == 2) || j == 0 && (l == 2 || j1 == 2) || k == 0 && (l == 2 || i1 == 2))) {
|
|
BlockPos blockposition2 = pos.offset(i, j, k);
|
|
BlockState iblockdata = world.getBlockState(blockposition2);
|
|
- Block[] ablock = ConduitBlockEntity.VALID_BLOCKS;
|
|
+ Block[] ablock = world.purpurConfig.conduitBlocks; // Purpur
|
|
int k1 = ablock.length;
|
|
|
|
for (int l1 = 0; l1 < k1; ++l1) {
|
|
@@ -191,7 +191,7 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
|
|
private static void applyEffects(Level world, BlockPos pos, List<BlockPos> activatingBlocks) {
|
|
int i = activatingBlocks.size();
|
|
- int j = i / 7 * 16;
|
|
+ int j = i / 7 * world.purpurConfig.conduitDistance; // Purpur
|
|
int k = pos.getX();
|
|
int l = pos.getY();
|
|
int i1 = pos.getZ();
|
|
@@ -222,21 +222,21 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
blockEntity.destroyTarget = ConduitBlockEntity.findDestroyTarget(world, pos, blockEntity.destroyTargetUUID);
|
|
blockEntity.destroyTargetUUID = null;
|
|
} else if (blockEntity.destroyTarget == null) {
|
|
- List<LivingEntity> list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos), (entityliving1) -> {
|
|
+ List<LivingEntity> list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos, world), (entityliving1) -> { // Purpur
|
|
return entityliving1 instanceof Enemy && entityliving1.isInWaterOrRain();
|
|
});
|
|
|
|
if (!list1.isEmpty()) {
|
|
blockEntity.destroyTarget = (LivingEntity) list1.get(world.random.nextInt(list1.size()));
|
|
}
|
|
- } else if (!blockEntity.destroyTarget.isAlive() || !pos.closerThan(blockEntity.destroyTarget.blockPosition(), 8.0D)) {
|
|
+ } else if (!blockEntity.destroyTarget.isAlive() || !pos.closerThan(blockEntity.destroyTarget.blockPosition(), world.purpurConfig.conduitDamageDistance)) { // Purpur
|
|
blockEntity.destroyTarget = null;
|
|
}
|
|
|
|
if (blockEntity.destroyTarget != null) {
|
|
// CraftBukkit start
|
|
CraftEventFactory.blockDamage = CraftBlock.at(world, pos);
|
|
- if (blockEntity.destroyTarget.hurt(world.damageSources().magic(), 4.0F)) {
|
|
+ if (blockEntity.destroyTarget.hurt(world.damageSources().magic(), world.purpurConfig.conduitDamageAmount)) { // Purpur
|
|
world.playSound((Player) null, blockEntity.destroyTarget.getX(), blockEntity.destroyTarget.getY(), blockEntity.destroyTarget.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
}
|
|
CraftEventFactory.blockDamage = null;
|
|
@@ -262,16 +262,22 @@ public class ConduitBlockEntity extends BlockEntity {
|
|
}
|
|
|
|
private static AABB getDestroyRangeAABB(BlockPos pos) {
|
|
+ // Purpur start
|
|
+ return getDestroyRangeAABB(pos, null);
|
|
+ }
|
|
+
|
|
+ private static AABB getDestroyRangeAABB(BlockPos pos, Level level) {
|
|
+ // Purpur end
|
|
int i = pos.getX();
|
|
int j = pos.getY();
|
|
int k = pos.getZ();
|
|
|
|
- return (new AABB((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1))).inflate(8.0D);
|
|
+ return (new AABB((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1))).inflate(level == null ? 8.0D : level.purpurConfig.conduitDamageDistance); // Purpur
|
|
}
|
|
|
|
@Nullable
|
|
private static LivingEntity findDestroyTarget(Level world, BlockPos pos, UUID uuid) {
|
|
- List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos), (entityliving) -> {
|
|
+ List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos, world), (entityliving) -> { // Purpur
|
|
return entityliving.getUUID().equals(uuid);
|
|
});
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java
|
|
index 65e1381bb2d10bd212463feb602c60f8fdb9ade1..b7370e64fd0d50e8725d7d5afc30af2e8bc8455d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java
|
|
@@ -24,6 +24,7 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable
|
|
public float tRot;
|
|
private static final RandomSource RANDOM = RandomSource.create();
|
|
private Component name;
|
|
+ private int lapis = 0; // Purpur
|
|
|
|
public EnchantmentTableBlockEntity(BlockPos pos, BlockState state) {
|
|
super(BlockEntityType.ENCHANTING_TABLE, pos, state);
|
|
@@ -35,6 +36,7 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable
|
|
if (this.hasCustomName()) {
|
|
nbt.putString("CustomName", Component.Serializer.toJson(this.name));
|
|
}
|
|
+ nbt.putInt("Purpur.Lapis", this.lapis); // Purpur
|
|
|
|
}
|
|
|
|
@@ -44,6 +46,7 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable
|
|
if (nbt.contains("CustomName", 8)) {
|
|
this.name = io.papermc.paper.util.MCUtil.getBaseComponentFromNbt("CustomName", nbt); // Paper - Catch ParseException
|
|
}
|
|
+ this.lapis = nbt.getInt("Purpur.Lapis"); // Purpur
|
|
|
|
}
|
|
|
|
@@ -117,4 +120,14 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable
|
|
public Component getCustomName() {
|
|
return this.name;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ public int getLapis() {
|
|
+ return this.lapis;
|
|
+ }
|
|
+
|
|
+ public void setLapis(int lapis) {
|
|
+ this.lapis = lapis;
|
|
+ }
|
|
+ // Purpur
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
|
|
index 4da4edae517a0efec6e03a719ec47b700509dab1..9e760a8e8244b15daaf0abdfc5f8a51d5c663e12 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
|
|
@@ -203,6 +203,23 @@ public class SignBlockEntity extends BlockEntity implements CommandSource { // C
|
|
return ClientboundBlockEntityDataPacket.create(this);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public ClientboundBlockEntityDataPacket getTranslatedUpdatePacket(boolean filtered) {
|
|
+ final CompoundTag nbt = new CompoundTag();
|
|
+ this.saveAdditional(nbt);
|
|
+ final Component[] lines = getMessages(filtered);
|
|
+ for (int i = 0; i < 4; i++) {
|
|
+ final var component = io.papermc.paper.adventure.PaperAdventure.asAdventure(lines[i]);
|
|
+ final String line = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacyAmpersand().serialize(component);
|
|
+ final var text = net.kyori.adventure.text.Component.text(line);
|
|
+ final String json = net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().serialize(text);
|
|
+ nbt.putString("Text" + (i + 1), json);
|
|
+ }
|
|
+ nbt.putString("PurpurEditor", "true");
|
|
+ return ClientboundBlockEntityDataPacket.create(this, entity -> nbt);
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public CompoundTag getUpdateTag() {
|
|
return this.saveWithoutMetadata();
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
|
|
index c2bc747e9b9dd02971c13cb88e7aebeca8c0f2aa..a4cd9869988f414ce1bf14d38442f78207e3f048 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
|
|
@@ -178,6 +178,15 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity {
|
|
|
|
public static void teleportEntity(Level world, BlockPos pos, BlockState state, Entity entity, TheEndGatewayBlockEntity blockEntity) {
|
|
if (world instanceof ServerLevel && !blockEntity.isCoolingDown()) {
|
|
+ if (!entity.canChangeDimensions()) return; // Purpur
|
|
+ // Purpur start
|
|
+ if (world.purpurConfig.imposeTeleportRestrictionsOnGateways && (entity.isVehicle() || entity.isPassenger())) {
|
|
+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_GATEWAY).callEvent()) {
|
|
+ teleportEntity(world, pos, state, entity, blockEntity);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Purpur end
|
|
ServerLevel worldserver = (ServerLevel) world;
|
|
|
|
blockEntity.teleportCooldown = 100;
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java
|
|
index 744d91546d1a810f60a43c15ed74b4158f341a4a..354538daefa603f6df5a139b6bff87dbb4cef178 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java
|
|
@@ -86,7 +86,7 @@ public class PistonStructureResolver {
|
|
return true;
|
|
} else {
|
|
int i = 1;
|
|
- if (i + this.toPush.size() > 12) {
|
|
+ if (i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur
|
|
return false;
|
|
} else {
|
|
while(isSticky(blockState)) {
|
|
@@ -98,7 +98,7 @@ public class PistonStructureResolver {
|
|
}
|
|
|
|
++i;
|
|
- if (i + this.toPush.size() > 12) {
|
|
+ if (i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur
|
|
return false;
|
|
}
|
|
}
|
|
@@ -142,7 +142,7 @@ public class PistonStructureResolver {
|
|
return true;
|
|
}
|
|
|
|
- if (this.toPush.size() >= 12) {
|
|
+ if (this.toPush.size() >= this.level.purpurConfig.pistonBlockPushLimit) { // Purpur
|
|
return false;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
index 505503a3f59d4b747649275c6f6faa504b7c7b64..bee42ce7c1cb0f5ebd4890c02bc9c5dd727f7fd6 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
@@ -78,9 +78,9 @@ import net.minecraft.world.phys.shapes.VoxelShape;
|
|
public abstract class BlockBehaviour implements FeatureElement {
|
|
|
|
protected static final Direction[] UPDATE_SHAPE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP};
|
|
- protected final Material material;
|
|
+ public final Material material; // Purpur - protected -> public
|
|
public final boolean hasCollision;
|
|
- protected final float explosionResistance;
|
|
+ public float explosionResistance; // Purpur - protected final -> public
|
|
protected final boolean isRandomlyTicking;
|
|
protected final SoundType soundType;
|
|
protected final float friction;
|
|
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 13594b96cc8f451723c3598ef302ccee8e01bcac..e9d92bf484fb0d2fcb66a7c424eced109bfa031d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
@@ -129,7 +129,7 @@ public class LevelChunk extends ChunkAccess {
|
|
this.blockTicks = blockTickScheduler;
|
|
this.fluidTicks = fluidTickScheduler;
|
|
|
|
- this.lightningTick = this.level.getThreadUnsafeRandom().nextInt(100000) << 1; // Pufferfish - initialize lightning tick
|
|
+ this.lightningTick = java.util.concurrent.ThreadLocalRandom.current().nextInt(100000) << 1; // Pufferfish - initialize lightning tick // Purpur - any random will do
|
|
}
|
|
|
|
// CraftBukkit start
|
|
@@ -925,7 +925,7 @@ public class LevelChunk extends ChunkAccess {
|
|
this.chunkHolder.getEntityChunk().callEntitiesLoadEvent(); // Paper - rewrite chunk system
|
|
|
|
if (this.needsDecoration) {
|
|
- try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper
|
|
+ //try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper // Purpur
|
|
this.needsDecoration = false;
|
|
java.util.Random random = new java.util.Random();
|
|
random.setSeed(this.level.getSeed());
|
|
@@ -945,7 +945,7 @@ public class LevelChunk extends ChunkAccess {
|
|
}
|
|
}
|
|
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk));
|
|
- } // Paper
|
|
+ //} // Paper // Purpur
|
|
}
|
|
}
|
|
}
|
|
@@ -1315,10 +1315,10 @@ public class LevelChunk extends ChunkAccess {
|
|
|
|
if (LevelChunk.this.isTicking(blockposition)) {
|
|
try {
|
|
- ProfilerFiller gameprofilerfiller = LevelChunk.this.level.getProfiler();
|
|
+ //ProfilerFiller gameprofilerfiller = LevelChunk.this.level.getProfiler(); // Purpur
|
|
|
|
- gameprofilerfiller.push(this::getType);
|
|
- this.blockEntity.tickTimer.startTiming(); // Spigot
|
|
+ //gameprofilerfiller.push(this::getType); // Purpur
|
|
+ //this.blockEntity.tickTimer.startTiming(); // Spigot // Purpur
|
|
BlockState iblockdata = LevelChunk.this.getBlockState(blockposition);
|
|
|
|
if (this.blockEntity.getType().isValid(iblockdata)) {
|
|
@@ -1329,7 +1329,7 @@ public class LevelChunk extends ChunkAccess {
|
|
LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata});
|
|
}
|
|
|
|
- gameprofilerfiller.pop();
|
|
+ //gameprofilerfiller.pop(); // Purpur
|
|
} catch (Throwable throwable) {
|
|
if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
|
// Paper start - Prevent tile entity and entity crashes
|
|
@@ -1340,7 +1340,7 @@ public class LevelChunk extends ChunkAccess {
|
|
// Paper end
|
|
// Spigot start
|
|
} finally {
|
|
- this.blockEntity.tickTimer.stopTiming();
|
|
+ //this.blockEntity.tickTimer.stopTiming(); // Purpur
|
|
// Spigot end
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
|
index 060e064625969610539dbf969ce773b877a7c579..32cd9df202704cdfb8fa06aaf0e738d483054feb 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
|
@@ -112,6 +112,7 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
|
|
ListTag listTag = new ListTag();
|
|
final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper
|
|
entities.forEach((entity) -> { // diff here: use entities parameter
|
|
+ if (!entity.canSaveToDisk()) return; // Purpur
|
|
// Paper start
|
|
final EntityType<?> entityType = entity.getType();
|
|
final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
|
|
diff --git a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
index e8ae4449696d73c8c9b8b27d4d2e20db933a72cc..f55c50f6637a4f930b15565d6ac82bb4f27b9059 100644
|
|
--- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
|
|
@@ -51,7 +51,7 @@ public class PhantomSpawner implements CustomSpawner {
|
|
int spawnAttemptMaxSeconds = world.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds;
|
|
this.nextTick += (spawnAttemptMinSeconds + randomsource.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20;
|
|
// Paper end
|
|
- if (world.getSkyDarken() < 5 && world.dimensionType().hasSkyLight()) {
|
|
+ if (world.getSkyDarken() < world.purpurConfig.phantomSpawnMinSkyDarkness && world.dimensionType().hasSkyLight()) { // Purpur
|
|
return 0;
|
|
} else {
|
|
int i = 0;
|
|
@@ -63,10 +63,10 @@ public class PhantomSpawner implements CustomSpawner {
|
|
if (!entityhuman.isSpectator() && (!world.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !entityhuman.isCreative())) { // Paper
|
|
BlockPos blockposition = entityhuman.blockPosition();
|
|
|
|
- if (!world.dimensionType().hasSkyLight() || blockposition.getY() >= world.getSeaLevel() && world.canSeeSky(blockposition)) {
|
|
+ if (!world.dimensionType().hasSkyLight() || (!world.purpurConfig.phantomSpawnOnlyAboveSeaLevel || blockposition.getY() >= world.getSeaLevel()) && (!world.purpurConfig.phantomSpawnOnlyWithVisibleSky || world.canSeeSky(blockposition))) { // Purpur
|
|
DifficultyInstance difficultydamagescaler = world.getCurrentDifficultyAt(blockposition);
|
|
|
|
- if (difficultydamagescaler.isHarderThan(randomsource.nextFloat() * 3.0F)) {
|
|
+ if (difficultydamagescaler.isHarderThan(randomsource.nextFloat() * (float) world.purpurConfig.phantomSpawnLocalDifficultyChance)) { // Purpur
|
|
ServerStatsCounter serverstatisticmanager = ((ServerPlayer) entityhuman).getStats();
|
|
int j = Mth.clamp(serverstatisticmanager.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE);
|
|
boolean flag2 = true;
|
|
@@ -78,7 +78,7 @@ public class PhantomSpawner implements CustomSpawner {
|
|
|
|
if (NaturalSpawner.isValidEmptySpawnBlock(world, blockposition1, iblockdata, fluid, EntityType.PHANTOM)) {
|
|
SpawnGroupData groupdataentity = null;
|
|
- int k = 1 + randomsource.nextInt(difficultydamagescaler.getDifficulty().getId() + 1);
|
|
+ int k = world.purpurConfig.phantomSpawnMinPerAttempt + world.random.nextInt((world.purpurConfig.phantomSpawnMaxPerAttempt < 0 ? difficultydamagescaler.getDifficulty().getId() : world.purpurConfig.phantomSpawnMaxPerAttempt - world.purpurConfig.phantomSpawnMinPerAttempt) + 1); // Purpur
|
|
|
|
for (int l = 0; l < k; ++l) {
|
|
// Paper start
|
|
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 6063665b8848a2cd9f0b262eed36a9dd48db6035..5536b9e36b4ea99e2aaa62690b5bf00208291a58 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
@@ -226,7 +226,7 @@ public abstract class FlowingFluid extends Fluid {
|
|
}
|
|
}
|
|
|
|
- if (this.canConvertToSource(world) && j >= 2) {
|
|
+ if (this.canConvertToSource(world) && j >= getRequiredSources(world)) {
|
|
BlockState iblockdata2 = world.getBlockState(pos.below());
|
|
FluidState fluid1 = iblockdata2.getFluidState();
|
|
|
|
@@ -324,6 +324,12 @@ public abstract class FlowingFluid extends Fluid {
|
|
|
|
protected abstract boolean canConvertToSource(Level world);
|
|
|
|
+ // Purpur start
|
|
+ protected int getRequiredSources(Level level) {
|
|
+ return 2;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
protected void spreadTo(LevelAccessor world, BlockPos pos, BlockState state, Direction direction, FluidState fluidState) {
|
|
if (state.getBlock() instanceof LiquidBlockContainer) {
|
|
((LiquidBlockContainer) state.getBlock()).placeLiquid(world, pos, state, fluidState);
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java
|
|
index 783e315d92227cbcb5cd207b0a06a12e0778d14b..b05b4d3d97bca159c297f150005b5ab5bf6087e0 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java
|
|
@@ -180,7 +180,7 @@ public abstract class LavaFluid extends FlowingFluid {
|
|
|
|
@Override
|
|
public int getTickDelay(LevelReader world) {
|
|
- return world.dimensionType().ultraWarm() ? 10 : 30;
|
|
+ return world.dimensionType().ultraWarm() ? world.getWorldBorder().world.purpurConfig.lavaSpeedNether : world.getWorldBorder().world.purpurConfig.lavaSpeedNotNether; // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -198,6 +198,13 @@ public abstract class LavaFluid extends FlowingFluid {
|
|
world.levelEvent(1501, pos, 0);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ protected int getRequiredSources(Level level) {
|
|
+ return level.purpurConfig.lavaInfiniteRequiredSources;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
protected boolean canConvertToSource(Level world) {
|
|
return world.getGameRules().getBoolean(GameRules.RULE_LAVA_SOURCE_CONVERSION);
|
|
@@ -232,7 +239,7 @@ public abstract class LavaFluid extends FlowingFluid {
|
|
|
|
@Override
|
|
protected float getExplosionResistance() {
|
|
- return 100.0F;
|
|
+ return Blocks.LAVA.getExplosionResistance(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/WaterFluid.java b/src/main/java/net/minecraft/world/level/material/WaterFluid.java
|
|
index 82e85fbbd45244d02df90fa00c9046e7f51275a2..0f16deddd8cbb506ef7886f57ae640a42e841703 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java
|
|
@@ -64,6 +64,13 @@ public abstract class WaterFluid extends FlowingFluid {
|
|
return world.getGameRules().getBoolean(GameRules.RULE_WATER_SOURCE_CONVERSION);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ protected int getRequiredSources(Level level) {
|
|
+ return level.purpurConfig.waterInfiniteRequiredSources;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
// Paper start
|
|
@Override
|
|
protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state, BlockPos source) {
|
|
@@ -109,7 +116,7 @@ public abstract class WaterFluid extends FlowingFluid {
|
|
|
|
@Override
|
|
protected float getExplosionResistance() {
|
|
- return 100.0F;
|
|
+ return Blocks.WATER.getExplosionResistance(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
|
|
index d23481453717f715124156b5d83f6448f720d049..a8af51a25b0f99c3a64d9150fdfcd6b818aa7581 100644
|
|
--- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
|
|
+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
|
|
@@ -53,8 +53,8 @@ public class PathFinder {
|
|
@Nullable
|
|
// Paper start - optimize collection
|
|
private Path findPath(ProfilerFiller profiler, Node startNode, List<Map.Entry<Target, BlockPos>> positions, float followRange, int distance, float rangeMultiplier) {
|
|
- profiler.push("find_path");
|
|
- profiler.markForCharting(MetricCategory.PATH_FINDING);
|
|
+ //profiler.push("find_path"); // Purpur
|
|
+ //profiler.markForCharting(MetricCategory.PATH_FINDING); // Purpur
|
|
// Set<Target> set = positions.keySet();
|
|
startNode.g = 0.0F;
|
|
startNode.h = this.getBestH(startNode, positions); // Paper - optimize collection
|
|
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
|
index 94a0fde36dda9404e5eb62d323c71dac1929a46b..a7578e112e80ed2585a7eb4fff9542f6068546be 100644
|
|
--- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
|
+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
|
@@ -243,7 +243,7 @@ public class WalkNodeEvaluator extends NodeEvaluator {
|
|
}
|
|
|
|
if (blockPathTypes != BlockPathTypes.WALKABLE && (!this.isAmphibious() || blockPathTypes != BlockPathTypes.WATER)) {
|
|
- if ((node == null || node.costMalus < 0.0F) && maxYStep > 0 && (blockPathTypes != BlockPathTypes.FENCE || this.canWalkOverFences()) && blockPathTypes != BlockPathTypes.UNPASSABLE_RAIL && blockPathTypes != BlockPathTypes.TRAPDOOR && blockPathTypes != BlockPathTypes.POWDER_SNOW) {
|
|
+ if ((node == null || node.costMalus < 0.0F) && maxYStep > 0 && (blockPathTypes != BlockPathTypes.FENCE || this.canWalkOverFences()) && (this.mob.level.purpurConfig.mobsIgnoreRails || blockPathTypes != BlockPathTypes.UNPASSABLE_RAIL) && blockPathTypes != BlockPathTypes.TRAPDOOR && blockPathTypes != BlockPathTypes.POWDER_SNOW) { // Purpur
|
|
node = this.findAcceptedNode(x, y + 1, z, maxYStep - 1, prevFeetY, direction, nodeType);
|
|
if (node != null && (node.type == BlockPathTypes.OPEN || node.type == BlockPathTypes.WALKABLE) && this.mob.getBbWidth() < 1.0F) {
|
|
double g = (double)(x - direction.getStepX()) + 0.5D;
|
|
@@ -463,7 +463,7 @@ public class WalkNodeEvaluator extends NodeEvaluator {
|
|
return BlockPathTypes.BLOCKED;
|
|
} else {
|
|
// Paper end
|
|
- if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH)) {
|
|
+ if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH) || blockState.is(Blocks.STONECUTTER)) { // Purpur
|
|
return BlockPathTypes.DANGER_OTHER;
|
|
}
|
|
|
|
@@ -493,7 +493,7 @@ public class WalkNodeEvaluator extends NodeEvaluator {
|
|
} else if (!blockState.is(BlockTags.TRAPDOORS) && !blockState.is(Blocks.LILY_PAD) && !blockState.is(Blocks.BIG_DRIPLEAF)) {
|
|
if (blockState.is(Blocks.POWDER_SNOW)) {
|
|
return BlockPathTypes.POWDER_SNOW;
|
|
- } else if (!blockState.is(Blocks.CACTUS) && !blockState.is(Blocks.SWEET_BERRY_BUSH)) {
|
|
+ } else if (!blockState.is(Blocks.CACTUS) && !blockState.is(Blocks.SWEET_BERRY_BUSH) && !blockState.is(Blocks.STONECUTTER)) { // Purpur
|
|
if (blockState.is(Blocks.HONEY_BLOCK)) {
|
|
return BlockPathTypes.STICKY_HONEY;
|
|
} else if (blockState.is(Blocks.COCOA)) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/portal/PortalShape.java b/src/main/java/net/minecraft/world/level/portal/PortalShape.java
|
|
index c461e0d04047db9c0c5ecc04063cebd38bf96ec2..e7554ec800f321e4e34c926c53f2375a8c3aa979 100644
|
|
--- a/src/main/java/net/minecraft/world/level/portal/PortalShape.java
|
|
+++ b/src/main/java/net/minecraft/world/level/portal/PortalShape.java
|
|
@@ -34,7 +34,7 @@ public class PortalShape {
|
|
private static final int MIN_HEIGHT = 3;
|
|
public static final int MAX_HEIGHT = 21;
|
|
private static final BlockBehaviour.StatePredicate FRAME = (iblockdata, iblockaccess, blockposition) -> {
|
|
- return iblockdata.is(Blocks.OBSIDIAN);
|
|
+ return iblockdata.is(Blocks.OBSIDIAN) || (org.purpurmc.purpur.PurpurConfig.cryingObsidianValidForPortalFrame && iblockdata.is(Blocks.CRYING_OBSIDIAN)); // Purpur
|
|
};
|
|
private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F;
|
|
private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0D;
|
|
diff --git a/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java b/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java
|
|
index 31918fa2eb38e42a5ea5366e559f25ea9d7d59ae..15d8e9261a89da30529ac347462c520920ca4e7d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java
|
|
+++ b/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java
|
|
@@ -49,6 +49,13 @@ public class LootingEnchantFunction extends LootItemConditionalFunction {
|
|
|
|
if (entity instanceof LivingEntity) {
|
|
int i = EnchantmentHelper.getMobLooting((LivingEntity) entity);
|
|
+ // Purpur start
|
|
+ if (org.purpurmc.purpur.PurpurConfig.fixProjectileLootingTransfer &&
|
|
+ context.getParamOrNull(LootContextParams.DIRECT_KILLER_ENTITY)
|
|
+ instanceof net.minecraft.world.entity.projectile.AbstractArrow arrow) {
|
|
+ i = arrow.lootingLevel;
|
|
+ }
|
|
+ // Purpur end
|
|
// CraftBukkit start - use lootingModifier if set by plugin
|
|
if (context.hasParam(LootContextParams.LOOTING_MOD)) {
|
|
i = context.getParamOrNull(LootContextParams.LOOTING_MOD);
|
|
diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java
|
|
index ffc76354ead6937daf366c3d87bcb51d3e4c47f5..5b98d42b5d6bc07265fbb017e51a6281c148436a 100644
|
|
--- a/src/main/java/net/minecraft/world/phys/AABB.java
|
|
+++ b/src/main/java/net/minecraft/world/phys/AABB.java
|
|
@@ -374,4 +374,10 @@ public class AABB {
|
|
public static AABB ofSize(Vec3 center, double dx, double dy, double dz) {
|
|
return new AABB(center.x - dx / 2.0D, center.y - dy / 2.0D, center.z - dz / 2.0D, center.x + dx / 2.0D, center.y + dy / 2.0D, center.z + dz / 2.0D);
|
|
}
|
|
+
|
|
+ // Purpur - tuinity added method
|
|
+ public final AABB offsetY(double dy) {
|
|
+ return new AABB(this.minX, this.minY + dy, this.minZ, this.maxX, this.maxY + dy, this.maxZ);
|
|
+ }
|
|
+ // Purpur
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
|
|
index 1d7c663fa0e550bd0cfb9a4b83ccd7e2968666f0..0043c0087896a6df6910b0500da37d84b287c901 100644
|
|
--- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java
|
|
+++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java
|
|
@@ -86,20 +86,20 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
|
|
}
|
|
|
|
public void tick(long time, int maxTicks, BiConsumer<BlockPos, T> ticker) {
|
|
- ProfilerFiller profilerFiller = this.profiler.get();
|
|
- profilerFiller.push("collect");
|
|
- this.collectTicks(time, maxTicks, profilerFiller);
|
|
- profilerFiller.popPush("run");
|
|
- profilerFiller.incrementCounter("ticksToRun", this.toRunThisTick.size());
|
|
+ //ProfilerFiller profilerFiller = this.profiler.get(); // Purpur
|
|
+ //profilerFiller.push("collect"); // Purpur
|
|
+ this.collectTicks(time, maxTicks, null); // Purpur
|
|
+ //profilerFiller.popPush("run"); // Purpur
|
|
+ //profilerFiller.incrementCounter("ticksToRun", this.toRunThisTick.size()); // Purpur
|
|
this.runCollectedTicks(ticker);
|
|
- profilerFiller.popPush("cleanup");
|
|
+ //profilerFiller.popPush("cleanup"); // Purpur
|
|
this.cleanupAfterTick();
|
|
- profilerFiller.pop();
|
|
+ //profilerFiller.pop(); // Purpur
|
|
}
|
|
|
|
private void collectTicks(long time, int maxTicks, ProfilerFiller profiler) {
|
|
this.sortContainersToTick(time);
|
|
- profiler.incrementCounter("containersToTick", this.containersToTick.size());
|
|
+ //profiler.incrementCounter("containersToTick", this.containersToTick.size()); // Purpur
|
|
this.drainContainers(time, maxTicks);
|
|
this.rescheduleLeftoverContainers();
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
index 714afc98b5150907b45a00060be4e41582333204..312a6d90c0a09570aef24c205dc2ff277dcd4279 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
@@ -549,4 +549,213 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
manager.save();
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start - OfflinePlayer API
|
|
+ @Override
|
|
+ public boolean getAllowFlight() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getAllowFlight();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return false;
|
|
+ if (!data.contains("abilities")) return false;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getByte("mayfly") == (byte) 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setAllowFlight(boolean flight) {
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setAllowFlight(flight);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putByte("mayfly", (byte) (flight ? 1 : 0));
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isFlying() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.isFlying();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return false;
|
|
+ if (!data.contains("abilities")) return false;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getByte("flying") == (byte) 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setFlying(boolean value) {
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setFlying(value);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putByte("mayfly", (byte) (value ? 1 : 0));
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setFlySpeed(float value) throws IllegalArgumentException {
|
|
+ if (value < -1f || value > 1f) throw new IllegalArgumentException("FlySpeed needs to be between -1 and 1");
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setFlySpeed(value);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putFloat("flySpeed", value);
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getFlySpeed() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getFlySpeed();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return 0;
|
|
+ if (!data.contains("abilities")) return 0;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getFloat("flySpeed");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setWalkSpeed(float value) throws IllegalArgumentException {
|
|
+ if (value < -1f || value > 1f) throw new IllegalArgumentException("WalkSpeed needs to be between -1 and 1");
|
|
+ if (this.isOnline()) {
|
|
+ this.getPlayer().setWalkSpeed(value);
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return;
|
|
+ if (!data.contains("abilities")) return;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ abilities.putFloat("walkSpeed", value);
|
|
+ data.put("abilities", abilities);
|
|
+ save(data);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float getWalkSpeed() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getWalkSpeed();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return 0;
|
|
+ if (!data.contains("abilities")) return 0;
|
|
+ CompoundTag abilities = data.getCompound("abilities");
|
|
+ return abilities.getFloat("walkSpeed");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Location getLocation() {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().getLocation();
|
|
+ } else {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return null;
|
|
+ long worldUUIDMost = data.getLong("WorldUUIDMost");
|
|
+ long worldUUIDLeast = data.getLong("WorldUUIDLeast");
|
|
+ net.minecraft.nbt.ListTag position = data.getList("Pos", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_DOUBLE);
|
|
+ net.minecraft.nbt.ListTag rotation = data.getList("Rotation", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_FLOAT);
|
|
+ UUID worldUuid = new UUID(worldUUIDMost, worldUUIDLeast);
|
|
+ org.bukkit.World world = server.getWorld(worldUuid);
|
|
+ double x = position.getDouble(0);
|
|
+ double y = position.getDouble(1);
|
|
+ double z = position.getDouble(2);
|
|
+ float yaw = rotation.getFloat(0);
|
|
+ float pitch = rotation.getFloat(1);
|
|
+ return new Location(world, x, y, z, yaw, pitch);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean teleportOffline(Location destination) {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleport(destination);
|
|
+ } else {
|
|
+ return setLocation(destination);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean teleportOffline(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause){
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleport(destination, cause);
|
|
+ } else {
|
|
+ return setLocation(destination);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination) {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleportAsync(destination);
|
|
+ } else {
|
|
+ return java.util.concurrent.CompletableFuture.completedFuture(setLocation(destination));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) {
|
|
+ if (this.isOnline()) {
|
|
+ return this.getPlayer().teleportAsync(destination, cause);
|
|
+ } else {
|
|
+ return java.util.concurrent.CompletableFuture.completedFuture(setLocation(destination));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean setLocation(Location location) {
|
|
+ CompoundTag data = this.getData();
|
|
+ if (data == null) return false;
|
|
+ data.putLong("WorldUUIDMost", location.getWorld().getUID().getMostSignificantBits());
|
|
+ data.putLong("WorldUUIDLeast", location.getWorld().getUID().getLeastSignificantBits());
|
|
+ net.minecraft.nbt.ListTag position = new net.minecraft.nbt.ListTag();
|
|
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getX()));
|
|
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getY()));
|
|
+ position.add(net.minecraft.nbt.DoubleTag.valueOf(location.getZ()));
|
|
+ data.put("Pos", position);
|
|
+ net.minecraft.nbt.ListTag rotation = new net.minecraft.nbt.ListTag();
|
|
+ rotation.add(net.minecraft.nbt.FloatTag.valueOf(location.getYaw()));
|
|
+ rotation.add(net.minecraft.nbt.FloatTag.valueOf(location.getPitch()));
|
|
+ data.put("Rotation", rotation);
|
|
+ save(data);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Safely replaces player's .dat file with provided CompoundTag
|
|
+ * @param compoundTag
|
|
+ */
|
|
+ private void save(CompoundTag compoundTag) {
|
|
+ File playerDir = server.console.playerDataStorage.getPlayerDir();
|
|
+ try {
|
|
+ File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir);
|
|
+ net.minecraft.nbt.NbtIo.writeCompressed(compoundTag, tempFile);
|
|
+ File playerDataFile = new File(playerDir, this.getUniqueId()+".dat");
|
|
+ File playerDataFileOld = new File(playerDir, this.getUniqueId()+".dat_old");
|
|
+ net.minecraft.Util.safeReplaceFile(playerDataFile, tempFile, playerDataFileOld);
|
|
+ } catch (java.io.IOException e) {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+ // Purpur end - OfflinePlayer API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index 6ff93df3e870ebc6e52f8b8a719908bfa16a839b..3310828edcabd2c24e3200dcb89d4e8ebd82cf16 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -262,7 +262,7 @@ import javax.annotation.Nullable; // Paper
|
|
import javax.annotation.Nonnull; // Paper
|
|
|
|
public final class CraftServer implements Server {
|
|
- private final String serverName = "Pufferfish"; // Paper // Pufferfish
|
|
+ private final String serverName = "Purpur"; // Paper // Pufferfish // Purpur
|
|
private final String serverVersion;
|
|
private final String bukkitVersion = Versioning.getBukkitVersion();
|
|
private final Logger logger = Logger.getLogger("Minecraft");
|
|
@@ -324,6 +324,20 @@ public final class CraftServer implements Server {
|
|
this.dataPackManager = new CraftDataPackManager(this.getServer().getPackRepository());
|
|
|
|
Bukkit.setServer(this);
|
|
+ // Purpur start
|
|
+ org.purpurmc.purpur.language.Language.setLanguage(new org.purpurmc.purpur.language.Language() {
|
|
+ private net.minecraft.locale.Language language = net.minecraft.locale.Language.getInstance();
|
|
+ @Override
|
|
+ public boolean has(@org.jetbrains.annotations.NotNull String key) {
|
|
+ return language.has(key);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @org.jetbrains.annotations.NotNull String getOrDefault(@org.jetbrains.annotations.NotNull String key) {
|
|
+ return language.getOrDefault(key);
|
|
+ }
|
|
+ });
|
|
+ // Purpur end
|
|
|
|
// Register all the Enchantments and PotionTypes now so we can stop new registration immediately after
|
|
Enchantments.SHARPNESS.getClass();
|
|
@@ -979,6 +993,7 @@ public final class CraftServer implements Server {
|
|
|
|
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
|
|
this.console.paperConfigurations.reloadConfigs(this.console);
|
|
+ org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur
|
|
for (ServerLevel world : this.console.getAllLevels()) {
|
|
// world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
|
|
world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
|
|
@@ -994,6 +1009,7 @@ public final class CraftServer implements Server {
|
|
}
|
|
}
|
|
world.spigotConfig.init(); // Spigot
|
|
+ world.purpurConfig.init(); // Purpur
|
|
}
|
|
|
|
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
|
|
@@ -1009,6 +1025,7 @@ public final class CraftServer implements Server {
|
|
this.reloadData();
|
|
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
|
|
io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper
|
|
+ org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur
|
|
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
|
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
|
|
|
@@ -1451,6 +1468,55 @@ public final class CraftServer implements Server {
|
|
return true;
|
|
}
|
|
|
|
+ // Purpur Start
|
|
+ @Override
|
|
+ public void addFuel(org.bukkit.Material material, int burnTime) {
|
|
+ Preconditions.checkArgument(burnTime > 0, "BurnTime must be greater than 0");
|
|
+ net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity.addFuel(net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)), burnTime);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void removeFuel(org.bukkit.Material material) {
|
|
+ net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity.removeFuel(net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration) {
|
|
+ sendBlockHighlight(location, duration, "", 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, int argb) {
|
|
+ sendBlockHighlight(location, duration, "", argb);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text) {
|
|
+ sendBlockHighlight(location, duration, text, 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, int argb) {
|
|
+ this.worlds.forEach((name, world) -> world.sendBlockHighlight(location, duration, text, argb));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) {
|
|
+ sendBlockHighlight(location, duration, "", color, transparency);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) {
|
|
+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range");
|
|
+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clearBlockHighlights() {
|
|
+ this.worlds.forEach((name, world) -> clearBlockHighlights());
|
|
+ }
|
|
+ // Purpur End
|
|
+
|
|
@Override
|
|
public List<Recipe> getRecipesFor(ItemStack result) {
|
|
Validate.notNull(result, "Result cannot be null");
|
|
@@ -2722,6 +2788,7 @@ public final class CraftServer implements Server {
|
|
@Override
|
|
public double[] getTPS() {
|
|
return new double[] {
|
|
+ net.minecraft.server.MinecraftServer.getServer().tps5s.getAverage(), // Purpur
|
|
net.minecraft.server.MinecraftServer.getServer().tps1.getAverage(),
|
|
net.minecraft.server.MinecraftServer.getServer().tps5.getAverage(),
|
|
net.minecraft.server.MinecraftServer.getServer().tps15.getAverage()
|
|
@@ -2768,6 +2835,18 @@ public final class CraftServer implements Server {
|
|
return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console);
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public YamlConfiguration getPurpurConfig() {
|
|
+ return org.purpurmc.purpur.PurpurConfig.config;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.Properties getServerProperties() {
|
|
+ return getProperties().properties;
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public void restart() {
|
|
org.spigotmc.RestartCommand.restart();
|
|
@@ -2981,4 +3060,16 @@ public final class CraftServer implements Server {
|
|
}
|
|
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public String getServerName() {
|
|
+ return this.getProperties().serverName;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isLagging() {
|
|
+ return getServer().lagging;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 8f0234296397ca2d4a607dcea6093c6c606dc7d2..68df53648c9b76cf1b6abcaa90c5e8938e9e0d05 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -2284,6 +2284,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
return (this.getHandle().dragonFight() == null) ? null : new CraftDragonBattle(this.getHandle().dragonFight());
|
|
}
|
|
|
|
+ // Purpur start
|
|
+ public float getLocalDifficultyAt(Location location) {
|
|
+ return getHandle().getCurrentDifficultyAt(io.papermc.paper.util.MCUtil.toBlockPosition(location)).getEffectiveDifficulty();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration) {
|
|
+ sendBlockHighlight(location, duration, "", 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, int argb) {
|
|
+ sendBlockHighlight(location, duration, "", argb);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text) {
|
|
+ sendBlockHighlight(location, duration, text, 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, int argb) {
|
|
+ net.minecraft.network.protocol.game.DebugPackets.sendGameTestAddMarker(getHandle(), io.papermc.paper.util.MCUtil.toBlockPosition(location), text, argb, duration);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) {
|
|
+ sendBlockHighlight(location, duration, "", color, transparency);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) {
|
|
+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range");
|
|
+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clearBlockHighlights() {
|
|
+ net.minecraft.network.protocol.game.DebugPackets.sendGameTestClearPacket(getHandle());
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
@Override
|
|
public PersistentDataContainer getPersistentDataContainer() {
|
|
return this.persistentDataContainer;
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
index bfa091f72d6f477bcaf63d364639a1b4df9b1987..288cf98287c6d3c073b9ab6696c3957c999cad32 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
|
|
@@ -173,6 +173,20 @@ public class Main {
|
|
.describedAs("Jar file");
|
|
// Paper end
|
|
|
|
+ // Purpur Start
|
|
+ acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings")
|
|
+ .withRequiredArg()
|
|
+ .ofType(File.class)
|
|
+ .defaultsTo(new File("purpur.yml"))
|
|
+ .describedAs("Yml file");
|
|
+
|
|
+ acceptsAll(asList("pufferfish", "pufferfish-settings"), "File for pufferfish settings")
|
|
+ .withRequiredArg()
|
|
+ .ofType(File.class)
|
|
+ .defaultsTo(new File("pufferfish.yml"))
|
|
+ .describedAs("Yml file");
|
|
+ // Purpur end
|
|
+
|
|
// Paper start
|
|
acceptsAll(asList("server-name"), "Name of the server")
|
|
.withRequiredArg()
|
|
@@ -284,7 +298,7 @@ public class Main {
|
|
System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); // Paper
|
|
}
|
|
|
|
- if (Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) {
|
|
+ if (false && Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) { // Purpur
|
|
Date buildDate = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(Main.class.getPackage().getImplementationVendor()); // Paper
|
|
|
|
Calendar deadline = Calendar.getInstance();
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
|
index 4e56018b64d11f76c8da43fd8f85c6de72204e36..9607675e6c5bff2183c4420d11fc63eeb5747fb6 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
|
@@ -21,7 +21,12 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co
|
|
|
|
@Override
|
|
public void sendMessage(String message) {
|
|
- this.sendRawMessage(message);
|
|
+ // Purpur start
|
|
+ String[] parts = message.split("\n");
|
|
+ for (String part : parts) {
|
|
+ this.sendRawMessage(part);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
|
|
@Override
|
|
@@ -91,7 +96,7 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co
|
|
// Paper start
|
|
@Override
|
|
public void sendMessage(final net.kyori.adventure.identity.Identity identity, final net.kyori.adventure.text.Component message, final net.kyori.adventure.audience.MessageType type) {
|
|
- this.sendRawMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message));
|
|
+ this.sendMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message)); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java
|
|
index 3d0ce0803e1da8a2681a3cb41096ac942ece54a1..bcd075a771c7f43c6d1549aeec2ccb20ee168b57 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java
|
|
@@ -59,6 +59,7 @@ public class CraftEnchantment extends Enchantment {
|
|
return EnchantmentTarget.CROSSBOW;
|
|
case VANISHABLE:
|
|
return EnchantmentTarget.VANISHABLE;
|
|
+ case BOW_AND_CROSSBOW: return EnchantmentTarget.BOW_AND_CROSSBOW; // Purpur
|
|
default:
|
|
return null;
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
|
|
index 75c7645fb5732c43d1da15181cf5c7ee4c3ecd6c..e7f5ea4d8d72672cf03483e720c6389425f28f6d 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
|
|
@@ -27,12 +27,12 @@ public class CraftEndermite extends CraftMonster implements Endermite {
|
|
|
|
@Override
|
|
public boolean isPlayerSpawned() {
|
|
- return false;
|
|
+ return getHandle().isPlayerSpawned(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
public void setPlayerSpawned(boolean playerSpawned) {
|
|
- // Nop
|
|
+ getHandle().setPlayerSpawned(playerSpawned); // Purpur
|
|
}
|
|
// Paper start
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
index 56c75029a94e8812c9e0ce5375aaa7cbcda90b87..97ea9612343e4288decd8daa9327a7e781877a8e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
@@ -209,6 +209,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
this.entity = entity;
|
|
}
|
|
|
|
+ @Override
|
|
+ public boolean isInDaylight() {
|
|
+ return getHandle().isSunBurnTick();
|
|
+ }
|
|
+
|
|
public static CraftEntity getEntity(CraftServer server, Entity entity) {
|
|
/*
|
|
* Order is *EXTREMELY* important -- keep it right! =D
|
|
@@ -586,6 +591,10 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
// Paper end
|
|
|
|
if ((!ignorePassengers && this.entity.isVehicle()) || this.entity.isRemoved()) { // Paper - Teleport passenger API
|
|
+ // Purpur start
|
|
+ if (!entity.isRemoved() && new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent())
|
|
+ return teleport(location, cause);
|
|
+ // Purpur end
|
|
return false;
|
|
}
|
|
|
|
@@ -1442,4 +1451,37 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
return !this.getHandle().level.noCollision(this.getHandle(), aabb);
|
|
}
|
|
// Paper End - Collision API
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public org.bukkit.entity.Player getRider() {
|
|
+ net.minecraft.world.entity.player.Player rider = getHandle().getRider();
|
|
+ return rider != null ? (org.bukkit.entity.Player) rider.getBukkitEntity() : null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasRider() {
|
|
+ return getHandle().getRider() != null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isRidable() {
|
|
+ return getHandle().isRidable();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isRidableInWater() {
|
|
+ return !getHandle().dismountsUnderwater();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToFire() {
|
|
+ return getHandle().fireImmune();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToFire(Boolean fireImmune) {
|
|
+ getHandle().immuneToFire = fireImmune;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
|
index 1b008e5217c5bbf566a213abb92e1c7c43a3a7c2..468023414b4a9119a3418b8e8a5e38375bbd2407 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
|
@@ -266,6 +266,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
|
|
@Override
|
|
public void recalculatePermissions() {
|
|
this.perm.recalculatePermissions();
|
|
+ getHandle().canPortalInstant = hasPermission("purpur.portal.instant"); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
|
|
index 2966d4d466f44751b2f02afda2273a708c12b251..55f19324f92f98e497da49d3022e0edfc2351461 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
|
|
@@ -33,4 +33,17 @@ public class CraftIronGolem extends CraftGolem implements IronGolem {
|
|
public EntityType getType() {
|
|
return EntityType.IRON_GOLEM;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ @org.jetbrains.annotations.Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return getHandle().getSummoner();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) {
|
|
+ getHandle().setSummoner(summoner);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
|
index a925b5c490e7129b27370aa57b5fad1cf05530c6..ea15690da167ec5e653da6f5afb55b33c45d1622 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
|
|
@@ -160,4 +160,51 @@ public class CraftItem extends CraftEntity implements Item {
|
|
public EntityType getType() {
|
|
return EntityType.DROPPED_ITEM;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public void setImmuneToCactus(boolean immuneToCactus) {
|
|
+ item.immuneToCactus = immuneToCactus;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToCactus() {
|
|
+ return item.immuneToCactus;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToExplosion(boolean immuneToExplosion) {
|
|
+ item.immuneToExplosion = immuneToExplosion;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToExplosion() {
|
|
+ return item.immuneToExplosion;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToFire(@org.jetbrains.annotations.Nullable Boolean immuneToFire) {
|
|
+ item.immuneToFire = (immuneToFire != null && immuneToFire);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToFire(boolean immuneToFire) {
|
|
+ this.setImmuneToFire((Boolean) immuneToFire);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToFire() {
|
|
+ return item.immuneToFire;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setImmuneToLightning(boolean immuneToLightning) {
|
|
+ item.immuneToLightning = immuneToLightning;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isImmuneToLightning() {
|
|
+ return item.immuneToLightning;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
index d43859f8aa7beed82dd3a146bb1086982cd0cda7..7ef5980f7321662aa7034a74c2f6926846425db9 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
@@ -444,7 +444,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
net.minecraft.server.level.ServerPlayer entityPlayer = killer == null ? null : ((CraftPlayer) killer).getHandle();
|
|
getHandle().lastHurtByPlayer = entityPlayer;
|
|
getHandle().lastHurtByMob = entityPlayer;
|
|
- getHandle().lastHurtByPlayerTime = entityPlayer == null ? 0 : 100; // 100 value taken from EntityLiving#damageEntity
|
|
+ getHandle().lastHurtByPlayerTime = entityPlayer == null ? 0 : getHandle().level.purpurConfig.mobLastHurtByPlayerTime; // 100 value taken from EntityLiving#damageEntity // Purpur
|
|
}
|
|
// Paper end
|
|
|
|
@@ -456,7 +456,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
@Override
|
|
public boolean addPotionEffect(PotionEffect effect, boolean force) {
|
|
org.spigotmc.AsyncCatcher.catchOp("effect add"); // Paper
|
|
- this.getHandle().addEffect(new MobEffectInstance(MobEffect.byId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon()), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon
|
|
+ this.getHandle().addEffect(new MobEffectInstance(MobEffect.byId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon(), effect.getKey()), EntityPotionEffectEvent.Cause.PLUGIN); // Purpur - add key // Paper - Don't ignore icon
|
|
return true;
|
|
}
|
|
|
|
@@ -477,7 +477,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
@Override
|
|
public PotionEffect getPotionEffect(PotionEffectType type) {
|
|
MobEffectInstance handle = this.getHandle().getEffect(MobEffect.byId(type.getId()));
|
|
- return (handle == null) ? null : new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible());
|
|
+ return (handle == null) ? null : new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible(), handle.getKey()); // Purpur - add key
|
|
}
|
|
|
|
@Override
|
|
@@ -489,7 +489,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
public Collection<PotionEffect> getActivePotionEffects() {
|
|
List<PotionEffect> effects = new ArrayList<PotionEffect>();
|
|
for (MobEffectInstance handle : this.getHandle().activeEffects.values()) {
|
|
- effects.add(new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible()));
|
|
+ effects.add(new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible(), handle.getKey())); // Purpur - add key
|
|
}
|
|
return effects;
|
|
}
|
|
@@ -884,7 +884,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
return EntityCategory.WATER;
|
|
}
|
|
|
|
- throw new UnsupportedOperationException("Unsupported monster type: " + type + ". This is a bug, report this to Spigot.");
|
|
+ throw new UnsupportedOperationException("Unsupported monster type: " + type + ". This is a bug, report this to Purpur."); // Purpur
|
|
}
|
|
|
|
@Override
|
|
@@ -1071,4 +1071,32 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
getHandle().knockback(strength, directionX, directionZ);
|
|
};
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public float getSafeFallDistance() {
|
|
+ return getHandle().safeFallDistance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSafeFallDistance(float safeFallDistance) {
|
|
+ getHandle().safeFallDistance = safeFallDistance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void broadcastItemBreak(org.bukkit.inventory.EquipmentSlot slot) {
|
|
+ if (slot == null) return;
|
|
+ getHandle().broadcastBreakEvent(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean shouldBurnInDay() {
|
|
+ return getHandle().shouldBurnInDay();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setShouldBurnInDay(boolean shouldBurnInDay) {
|
|
+ getHandle().setShouldBurnInDay(shouldBurnInDay);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
|
|
index 4d7a2c4c1001aefe9fcd4be8dbcb414f721bfff9..2c7716a9d65ebda209a144b82c2126b602aa9182 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
|
|
@@ -96,4 +96,16 @@ public class CraftLlama extends CraftChestedHorse implements Llama, com.destroys
|
|
return this.getHandle().caravanTail == null ? null : (Llama) this.getHandle().caravanTail.getBukkitEntity();
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean shouldJoinCaravan() {
|
|
+ return getHandle().shouldJoinCaravan;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setShouldJoinCaravan(boolean shouldJoinCaravan) {
|
|
+ getHandle().shouldJoinCaravan = shouldJoinCaravan;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
index be64633c8bcee96f2ad5247525cac965b7b031b1..e363891e8ab872ed24c557e3f94110f36c6fb277 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
@@ -534,10 +534,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
|
|
@Override
|
|
public void setPlayerListName(String name) {
|
|
+ // Purpur start
|
|
+ setPlayerListName(name, false);
|
|
+ }
|
|
+ public void setPlayerListName(String name, boolean useMM) {
|
|
+ // Purpur end
|
|
if (name == null) {
|
|
name = getName();
|
|
}
|
|
- this.getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromStringOrNull(name);
|
|
+ this.getHandle().listName = name.equals(getName()) ? null : useMM ? io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name)) : CraftChatMessage.fromStringOrNull(name); // Purpur
|
|
for (ServerPlayer player : (List<ServerPlayer>) server.getHandle().players) {
|
|
if (player.getBukkitEntity().canSee(this)) {
|
|
player.connection.send(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, this.getHandle()));
|
|
@@ -1358,6 +1363,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
}
|
|
|
|
if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API
|
|
+ // Purpur start
|
|
+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent())
|
|
+ return teleport(location, cause);
|
|
+ // Purpur end
|
|
return false;
|
|
}
|
|
|
|
@@ -2405,6 +2414,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
return this.getHandle().getAbilities().walkingSpeed * 2f;
|
|
}
|
|
|
|
+ // Purpur start - OfflinePlayer API
|
|
+ @Override
|
|
+ public boolean teleportOffline(@NotNull Location destination) {
|
|
+ return this.teleport(destination);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean teleportOffline(Location destination, PlayerTeleportEvent.TeleportCause cause) {
|
|
+ return this.teleport(destination, cause);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination) {
|
|
+ return this.teleportAsync(destination);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(@NotNull Location destination, PlayerTeleportEvent.TeleportCause cause) {
|
|
+ return this.teleportAsync(destination, cause);
|
|
+ }
|
|
+ // Purpur end - OfflinePlayer API
|
|
+
|
|
private void validateSpeed(float value) {
|
|
if (value < 0) {
|
|
if (value < -1f) {
|
|
@@ -3180,4 +3211,97 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
return this.spigot;
|
|
}
|
|
// Spigot end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean usesPurpurClient() {
|
|
+ return getHandle().purpurClient;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isAfk() {
|
|
+ return getHandle().isAfk();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setAfk(boolean setAfk) {
|
|
+ getHandle().setAfk(setAfk);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void resetIdleTimer() {
|
|
+ getHandle().resetLastActionTime();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isSpawnInvulnerable() {
|
|
+ return getHandle().isSpawnInvulnerable();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getSpawnInvulnerableTicks() {
|
|
+ return getHandle().spawnInvulnerableTime;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSpawnInvulnerableTicks(int spawnInvulnerableTime) {
|
|
+ getHandle().spawnInvulnerableTime = spawnInvulnerableTime;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration) {
|
|
+ sendBlockHighlight(location, duration, "", 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, int argb) {
|
|
+ sendBlockHighlight(location, duration, "", argb);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text) {
|
|
+ sendBlockHighlight(location, duration, text, 0x6400FF00);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, int argb) {
|
|
+ if (this.getHandle().connection == null) return;
|
|
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
|
+ buf.writeBlockPos(io.papermc.paper.util.MCUtil.toBlockPosition(location));
|
|
+ buf.writeInt(argb);
|
|
+ buf.writeUtf(text);
|
|
+ buf.writeInt(duration);
|
|
+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket(ClientboundCustomPayloadPacket.DEBUG_GAME_TEST_ADD_MARKER, buf));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) {
|
|
+ sendBlockHighlight(location, duration, "", color, transparency);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) {
|
|
+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range");
|
|
+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clearBlockHighlights() {
|
|
+ if (this.getHandle().connection == null) return;
|
|
+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket(ClientboundCustomPayloadPacket.DEBUG_GAME_TEST_CLEAR, new FriendlyByteBuf(io.netty.buffer.Unpooled.buffer())));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendDeathScreen(net.kyori.adventure.text.Component message) {
|
|
+ sendDeathScreen(message, null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void sendDeathScreen(net.kyori.adventure.text.Component message, org.bukkit.entity.Entity killer) {
|
|
+ if (this.getHandle().connection == null) return;
|
|
+ net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket packet = new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), killer == null ? -1 : killer.getEntityId(), null);
|
|
+ packet.adventure$message = message;
|
|
+ this.getHandle().connection.send(packet);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
|
|
index 42b7058d93fab8cbee49dba130734e1df9910096..5c6f55527cc0016f09b443528463b3906c433f8b 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
|
|
@@ -34,4 +34,17 @@ public class CraftSnowman extends CraftGolem implements Snowman, com.destroystok
|
|
public EntityType getType() {
|
|
return EntityType.SNOWMAN;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ @org.jetbrains.annotations.Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return getHandle().getSummoner();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) {
|
|
+ getHandle().setSummoner(summoner);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
|
index 4e880409b06086568627f3e930159f1abb979984..48fb7302b54f8e7f5c424210b550c03d4d071ea9 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
|
@@ -223,4 +223,11 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
|
|
getHandle().getGossips().gossips.clear();
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isLobotomized() {
|
|
+ return getHandle().isLobotomized();
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
|
|
index 1a21d30620f13a48976da5ead7edab201ea68b21..a50a04dc2009515032058562627eba8e4406c5bb 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
|
|
@@ -105,4 +105,17 @@ public class CraftWither extends CraftMonster implements Wither, com.destroystok
|
|
this.getHandle().makeInvulnerable();
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ @org.jetbrains.annotations.Nullable
|
|
+ public java.util.UUID getSummoner() {
|
|
+ return getHandle().getSummoner();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) {
|
|
+ getHandle().setSummoner(summoner);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
|
|
index e43fd3e59fd8c74828ae65965fade27f56beef65..b2f133c8baabba1cffa6e92ea0f854532f4c181b 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
|
|
@@ -63,4 +63,16 @@ public class CraftWolf extends CraftTameableAnimal implements Wolf {
|
|
public void setInterested(boolean flag) {
|
|
this.getHandle().setIsInterested(flag);
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean isRabid() {
|
|
+ return getHandle().isRabid();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setRabid(boolean isRabid) {
|
|
+ getHandle().setRabid(isRabid);
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
index a153c134cf26e86d49ef419eca35994539af0db3..e9599e0f3d2122c3843ebde81743bc8d558bfd30 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -563,6 +563,15 @@ public class CraftEventFactory {
|
|
// Paper end
|
|
craftServer.getPluginManager().callEvent(event);
|
|
|
|
+ // Purpur start
|
|
+ if (who != null) {
|
|
+ switch (action) {
|
|
+ case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> who.processClick(InteractionHand.MAIN_HAND);
|
|
+ case RIGHT_CLICK_BLOCK, RIGHT_CLICK_AIR -> who.processClick(InteractionHand.OFF_HAND);
|
|
+ }
|
|
+ }
|
|
+ // Purpur end
|
|
+
|
|
return event;
|
|
}
|
|
|
|
@@ -1000,6 +1009,7 @@ public class CraftEventFactory {
|
|
damageCause = DamageCause.ENTITY_EXPLOSION;
|
|
}
|
|
event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), entity.getBukkitEntity(), damageCause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API
|
|
+ damager.processClick(InteractionHand.MAIN_HAND); // Purpur
|
|
}
|
|
event.setCancelled(cancelled);
|
|
|
|
@@ -1114,6 +1124,7 @@ public class CraftEventFactory {
|
|
} else {
|
|
entity.lastDamageCancelled = true; // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Keep track if the event was canceled
|
|
}
|
|
+ damager.getHandle().processClick(InteractionHand.MAIN_HAND); // Purpur
|
|
return event;
|
|
}
|
|
|
|
@@ -1173,6 +1184,7 @@ public class CraftEventFactory {
|
|
EntityDamageEvent event;
|
|
if (damager != null) {
|
|
event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, modifiers, modifierFunctions, critical); // Paper - add critical damage API
|
|
+ damager.processClick(InteractionHand.MAIN_HAND); // Purpur
|
|
} else {
|
|
event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, modifiers, modifierFunctions);
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
|
index 04088918e172eecb8d53b0e6de9be0071ccf33b5..eddd6073e0feb7b046db1d169020ca067fdf689c 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
|
@@ -182,8 +182,19 @@ public class CraftContainer extends AbstractContainerMenu {
|
|
case PLAYER:
|
|
case CHEST:
|
|
case ENDER_CHEST:
|
|
+ // Purpur start
|
|
+ this.delegate = new ChestMenu(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? MenuType.GENERIC_9x6 : MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9);
|
|
+ break;
|
|
case BARREL:
|
|
- this.delegate = new ChestMenu(MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9);
|
|
+ this.delegate = new ChestMenu(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) {
|
|
+ case 6 -> MenuType.GENERIC_9x6;
|
|
+ case 5 -> MenuType.GENERIC_9x5;
|
|
+ case 4 -> MenuType.GENERIC_9x4;
|
|
+ case 2 -> MenuType.GENERIC_9x2;
|
|
+ case 1 -> MenuType.GENERIC_9x1;
|
|
+ default -> MenuType.GENERIC_9x3;
|
|
+ }, windowId, bottom, top, top.getContainerSize() / 9);
|
|
+ // Purpur end
|
|
break;
|
|
case DISPENSER:
|
|
case DROPPER:
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
|
index 092f6843e3b43d4c615d2eee344f5966e96ae850..cb0c851ab5fcf676da2397040835a94d4bdb4be1 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
|
@@ -83,7 +83,7 @@ public class CraftInventory implements Inventory {
|
|
|
|
@Override
|
|
public void setContents(ItemStack[] items) {
|
|
- if (this.getSize() < items.length) {
|
|
+ if (false && this.getSize() < items.length) { // Purpur
|
|
throw new IllegalArgumentException("Invalid inventory size; expected " + this.getSize() + " or less");
|
|
}
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
|
|
index 88d3ca586ff6905f18a8ab9f0e229f440ed44088..27dd4eb4781a3c75772860c11db886e1038cecd2 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
|
|
@@ -9,7 +9,7 @@ import org.bukkit.inventory.AnvilInventory;
|
|
public class CraftInventoryAnvil extends CraftResultInventory implements AnvilInventory {
|
|
|
|
private final Location location;
|
|
- private final AnvilMenu container;
|
|
+ public final AnvilMenu container; // Purpur - private -> public
|
|
|
|
public CraftInventoryAnvil(Location location, Container inventory, Container resultInventory, AnvilMenu container) {
|
|
super(inventory, resultInventory);
|
|
@@ -57,4 +57,26 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn
|
|
Preconditions.checkArgument(levels >= 0, "Maximum repair cost must be positive (or 0)");
|
|
container.maximumRepairCost = levels;
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Override
|
|
+ public boolean canBypassCost() {
|
|
+ return container.bypassCost;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setBypassCost(boolean bypassCost) {
|
|
+ container.bypassCost = bypassCost;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canDoUnsafeEnchants() {
|
|
+ return container.canDoUnsafeEnchants;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) {
|
|
+ container.canDoUnsafeEnchants = canDoUnsafeEnchants;
|
|
+ }
|
|
+ // Purpur end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
|
|
index 0d4348ce1c4ec9bb779eaebf8606ea578f17d2cb..486768894f130cd23905cc7a8be16ce705667bbb 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
|
|
@@ -13,6 +13,7 @@ import net.minecraft.nbt.ListTag;
|
|
import org.apache.commons.lang.Validate;
|
|
import org.bukkit.Color;
|
|
import org.bukkit.Material;
|
|
+import org.bukkit.NamespacedKey;
|
|
import org.bukkit.configuration.serialization.DelegateDeserialization;
|
|
import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta;
|
|
import org.bukkit.craftbukkit.potion.CraftPotionUtil;
|
|
@@ -42,6 +43,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
|
|
static final ItemMetaKey POTION_COLOR = new ItemMetaKey("CustomPotionColor", "custom-color");
|
|
static final ItemMetaKey ID = new ItemMetaKey("Id", "potion-id");
|
|
static final ItemMetaKey DEFAULT_POTION = new ItemMetaKey("Potion", "potion-type");
|
|
+ static final ItemMetaKey KEY = new ItemMetaKey("Key", "namespacedkey"); // Purpur - add key
|
|
|
|
// Having an initial "state" in ItemMeta seems bit dirty but the UNCRAFTABLE potion type
|
|
// is treated as the empty form of the meta because it represents an empty potion with no effect
|
|
@@ -92,7 +94,13 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
|
|
boolean ambient = effect.getBoolean(AMBIENT.NBT);
|
|
boolean particles = effect.contains(SHOW_PARTICLES.NBT, CraftMagicNumbers.NBT.TAG_BYTE) ? effect.getBoolean(SHOW_PARTICLES.NBT) : true;
|
|
boolean icon = effect.contains(SHOW_ICON.NBT, CraftMagicNumbers.NBT.TAG_BYTE) ? effect.getBoolean(SHOW_ICON.NBT) : particles;
|
|
- this.customEffects.add(new PotionEffect(type, duration, amp, ambient, particles, icon));
|
|
+ // Purpur start
|
|
+ NamespacedKey key = null;
|
|
+ if (tag.contains(KEY.NBT)) {
|
|
+ key = NamespacedKey.fromString(effect.getString(KEY.NBT));
|
|
+ }
|
|
+ this.customEffects.add(new PotionEffect(type, duration, amp, ambient, particles, icon, key));
|
|
+ // Purpur end
|
|
}
|
|
}
|
|
}
|
|
@@ -141,6 +149,11 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
|
|
effectData.putBoolean(AMBIENT.NBT, effect.isAmbient());
|
|
effectData.putBoolean(SHOW_PARTICLES.NBT, effect.hasParticles());
|
|
effectData.putBoolean(SHOW_ICON.NBT, effect.hasIcon());
|
|
+ // Purpur start
|
|
+ if (effect.hasKey()) {
|
|
+ effectData.putString(KEY.NBT, effect.getKey().toString());
|
|
+ }
|
|
+ // Purpur end
|
|
effectList.add(effectData);
|
|
}
|
|
}
|
|
@@ -202,7 +215,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
|
|
if (index != -1) {
|
|
if (overwrite) {
|
|
PotionEffect old = this.customEffects.get(index);
|
|
- if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient()) {
|
|
+ if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient() && old.getKey() == effect.getKey()) { // Purpur - add key
|
|
return false;
|
|
}
|
|
this.customEffects.set(index, effect);
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
|
|
index 8563fcf77eef0e1e354857b9a4256d78a523a8d0..b94c964790433cb7bd88ad16c5d82d1a8e39312d 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
|
|
@@ -29,6 +29,7 @@ public interface CraftRecipe extends Recipe {
|
|
} else if (bukkit instanceof RecipeChoice.ExactChoice) {
|
|
stack = new Ingredient(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.world.item.crafting.Ingredient.ItemValue(CraftItemStack.asNMSCopy(mat))));
|
|
stack.exact = true;
|
|
+ stack.predicate = ((RecipeChoice.ExactChoice) bukkit).getPredicate(); // Purpur
|
|
} else {
|
|
throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit);
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
|
|
index 110503062b3043cffa082a1cda6b8d57152869aa..3e7e06bd5e9e4ed45c9e3452eb04e946fac817d8 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
|
|
@@ -256,6 +256,7 @@ public final class CraftLegacy {
|
|
}
|
|
|
|
static {
|
|
+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressInitLegacyMaterialError) // Purpur
|
|
LOGGER.warn("Initializing Legacy Material Support. Unless you have legacy plugins and/or data this is a bug!"); // Paper - doesn't need to be an error
|
|
if (MinecraftServer.getServer() != null && MinecraftServer.getServer().isDebugging()) {
|
|
new Exception().printStackTrace();
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java
|
|
index acb69821a99aa69bce6d127e10976089c85be223..c5abd73981c5f4b41605eba0d44e6573dfd2a77a 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java
|
|
@@ -101,7 +101,7 @@ public class CraftPotionUtil {
|
|
|
|
public static MobEffectInstance fromBukkit(PotionEffect effect) {
|
|
MobEffect type = MobEffect.byId(effect.getType().getId());
|
|
- return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles());
|
|
+ return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.getKey()); // Purpur - add key
|
|
}
|
|
|
|
public static PotionEffect toBukkit(MobEffectInstance effect) {
|
|
@@ -110,7 +110,7 @@ public class CraftPotionUtil {
|
|
int duration = effect.getDuration();
|
|
boolean ambient = effect.isAmbient();
|
|
boolean particles = effect.isVisible();
|
|
- return new PotionEffect(type, duration, amp, ambient, particles);
|
|
+ return new PotionEffect(type, duration, amp, ambient, particles, effect.getKey()); // Purpur - add key
|
|
}
|
|
|
|
public static boolean equals(MobEffect mobEffect, PotionEffectType type) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
|
index cdefb2025eedea7e204d70d568adaf1c1ec4c03c..5402098dce0d64d3dceea51f248d7d366850a74f 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
|
@@ -504,7 +504,7 @@ public class CraftScheduler implements BukkitScheduler {
|
|
this.parsePending();
|
|
} else {
|
|
//this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper
|
|
- task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Paper"); // Paper
|
|
+ task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Purpur"); // Paper // Purpur
|
|
// We don't need to parse pending
|
|
// (async tasks must live with race-conditions if they attempt to cancel between these few lines of code)
|
|
}
|
|
@@ -516,10 +516,10 @@ public class CraftScheduler implements BukkitScheduler {
|
|
this.runners.remove(task.getTaskId());
|
|
}
|
|
}
|
|
- MinecraftTimings.bukkitSchedulerFinishTimer.startTiming(); // Paper
|
|
+ //MinecraftTimings.bukkitSchedulerFinishTimer.startTiming(); // Paper // Purpur
|
|
this.pending.addAll(temp);
|
|
temp.clear();
|
|
- MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper
|
|
+ //MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper // Purpur
|
|
//this.debugHead = this.debugHead.getNextHead(currentTick); // Paper
|
|
}
|
|
|
|
@@ -563,7 +563,7 @@ public class CraftScheduler implements BukkitScheduler {
|
|
}
|
|
|
|
void parsePending() { // Paper
|
|
- if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.startTiming(); // Paper
|
|
+ //if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.startTiming(); // Paper // Purpur
|
|
CraftTask head = this.head;
|
|
CraftTask task = head.getNext();
|
|
CraftTask lastTask = head;
|
|
@@ -582,7 +582,7 @@ public class CraftScheduler implements BukkitScheduler {
|
|
task.setNext(null);
|
|
}
|
|
this.head = lastTask;
|
|
- if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(); // Paper
|
|
+ //if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(); // Paper // Purpur
|
|
}
|
|
|
|
private boolean isReady(final int currentTick) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
|
|
index 3f45bab0e9f7b3697e6d9d1092a1e6e579f7066f..4f1cf281c4bf68c37982d390da8779dea78dab18 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
|
|
@@ -96,13 +96,13 @@ public class CraftTask implements BukkitTask, Runnable { // Spigot
|
|
|
|
@Override
|
|
public void run() {
|
|
- try (Timing ignored = timings.startTiming()) { // Paper
|
|
+ //try (Timing ignored = timings.startTiming()) { // Paper // Purpur
|
|
if (this.rTask != null) {
|
|
this.rTask.run();
|
|
} else {
|
|
this.cTask.accept(this);
|
|
}
|
|
- } // Paper
|
|
+ //} // Paper // Purpur
|
|
}
|
|
|
|
long getCreatedAt() {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
|
|
index 138407c2d4b0bc55ddb9aac5d2aa3edadda090fb..a6e9e503a496c18e2501b03ec84f4600c134a50c 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
|
|
@@ -115,7 +115,7 @@ public final class CraftScoreboardManager implements ScoreboardManager {
|
|
public void getScoreboardScores(ObjectiveCriteria criteria, String name, Consumer<Score> consumer) {
|
|
// Paper start - add timings for scoreboard search
|
|
// plugins leaking scoreboards will make this very expensive, let server owners debug it easily
|
|
- co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync();
|
|
+ //co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync(); // Purpur
|
|
try {
|
|
// Paper end - add timings for scoreboard search
|
|
for (CraftScoreboard scoreboard : this.scoreboards) {
|
|
@@ -123,7 +123,7 @@ public final class CraftScoreboardManager implements ScoreboardManager {
|
|
board.forAllObjectives(criteria, name, (score) -> consumer.accept(score));
|
|
}
|
|
} finally { // Paper start - add timings for scoreboard search
|
|
- co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync();
|
|
+ //co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync(); // Purpur
|
|
}
|
|
// Paper end - add timings for scoreboard search
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
|
index 1ecf065c5323f65401412cb98d4a0b622f356759..1bf9a11abf7884a421374cbca5ccc18480717b79 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
|
@@ -472,7 +472,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
|
|
|
@Override
|
|
public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
|
|
- return new gg.pufferfish.pufferfish.PufferfishVersionFetcher(); // Pufferfish
|
|
+ return new com.destroystokyo.paper.PaperVersionFetcher(); // Purpur
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
|
|
index 80553face9c70c2a3d897681e7761df85b22d464..99597258e8e88cd9e2c901c4ac3ff7faeeabee2b 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/gg.pufferfish.pufferfish/pufferfish-api/pom.properties"); // Pufferfish
|
|
+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/org.purpurmc.purpur/purpur-api/pom.properties"); // Pufferfish // Purpur
|
|
Properties properties = new Properties();
|
|
|
|
if (stream != null) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
|
|
index ec771c480156db393c326fa2fbdc2d432fb76f53..71940bf3a4162d12a422a5b3100ad8def85f95ac 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
|
|
@@ -23,7 +23,15 @@ public final class CommandPermissions {
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands);
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "stop", "Allows the user to stop the server", PermissionDefault.OP, commands);
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "list", "Allows the user to list all online players", PermissionDefault.OP, commands);
|
|
- DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "gamemode", "Allows the user to change the gamemode of another player", PermissionDefault.OP, commands);
|
|
+ // Purpur start
|
|
+ Permission gamemodeVanilla = DefaultPermissions.registerPermission(PREFIX + "gamemode", "Allows the user to change the gamemode", PermissionDefault.OP, commands);
|
|
+ for (net.minecraft.world.level.GameType gametype : net.minecraft.world.level.GameType.values()) {
|
|
+ Permission gamemodeSelf = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName(), "Allows the user to set " + gametype.getName() + " gamemode for self", PermissionDefault.OP);
|
|
+ Permission gamemodeOther = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName() + ".other", "Allows the user to set " + gametype.getName() + " gamemode for other players", PermissionDefault.OP);
|
|
+ gamemodeSelf.addParent(gamemodeOther, true);
|
|
+ gamemodeVanilla.addParent(gamemodeSelf, true);
|
|
+ }
|
|
+ // Purpur end
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "xp", "Allows the user to give themselves or others arbitrary values of experience", PermissionDefault.OP, commands);
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "defaultgamemode", "Allows the user to change the default gamemode of the server", PermissionDefault.OP, commands);
|
|
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "seed", "Allows the user to view the seed of the world", PermissionDefault.OP, commands);
|
|
diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..be8b44daa0141151c973917a774aa07721647ed1
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
|
|
@@ -0,0 +1,638 @@
|
|
+package org.purpurmc.purpur;
|
|
+
|
|
+import com.google.common.base.Throwables;
|
|
+import com.google.common.collect.ImmutableMap;
|
|
+import com.mojang.datafixers.util.Pair;
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.world.effect.MobEffect;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import net.minecraft.world.entity.EntityDimensions;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.food.FoodProperties;
|
|
+import net.minecraft.world.food.Foods;
|
|
+import net.minecraft.world.item.enchantment.Enchantment;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.configuration.ConfigurationSection;
|
|
+import org.bukkit.configuration.InvalidConfigurationException;
|
|
+import org.bukkit.configuration.file.YamlConfiguration;
|
|
+import org.purpurmc.purpur.command.PurpurCommand;
|
|
+import org.purpurmc.purpur.task.TPSBarTask;
|
|
+
|
|
+import java.io.File;
|
|
+import java.io.IOException;
|
|
+import java.lang.reflect.InvocationTargetException;
|
|
+import java.lang.reflect.Method;
|
|
+import java.lang.reflect.Modifier;
|
|
+import java.util.ArrayList;
|
|
+import java.util.Collections;
|
|
+import java.util.HashMap;
|
|
+import java.util.HashSet;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import java.util.Set;
|
|
+import java.util.logging.Level;
|
|
+
|
|
+@SuppressWarnings("unused")
|
|
+public class PurpurConfig {
|
|
+ private static final String HEADER = "This is the main configuration file for Purpur.\n"
|
|
+ + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
|
|
+ + "with caution, and make sure you know what each option does before configuring.\n"
|
|
+ + "\n"
|
|
+ + "If you need help with the configuration or have any questions related to Purpur,\n"
|
|
+ + "join us in our Discord guild.\n"
|
|
+ + "\n"
|
|
+ + "Website: https://purpurmc.org \n"
|
|
+ + "Docs: https://purpurmc.org/docs \n";
|
|
+ private static File CONFIG_FILE;
|
|
+ public static YamlConfiguration config;
|
|
+
|
|
+ private static Map<String, Command> commands;
|
|
+
|
|
+ public static int version;
|
|
+ static boolean verbose;
|
|
+
|
|
+ public static void init(File configFile) {
|
|
+ CONFIG_FILE = configFile;
|
|
+ config = new YamlConfiguration();
|
|
+ try {
|
|
+ config.load(CONFIG_FILE);
|
|
+ } catch (IOException ignore) {
|
|
+ } catch (InvalidConfigurationException ex) {
|
|
+ Bukkit.getLogger().log(Level.SEVERE, "Could not load purpur.yml, please correct your syntax errors", ex);
|
|
+ throw Throwables.propagate(ex);
|
|
+ }
|
|
+ config.options().header(HEADER);
|
|
+ config.options().copyDefaults(true);
|
|
+ verbose = getBoolean("verbose", false);
|
|
+
|
|
+ commands = new HashMap<>();
|
|
+ commands.put("purpur", new PurpurCommand("purpur"));
|
|
+
|
|
+ version = getInt("config-version", 32);
|
|
+ set("config-version", 32);
|
|
+
|
|
+ readConfig(PurpurConfig.class, null);
|
|
+
|
|
+ Blocks.rebuildCache();
|
|
+ }
|
|
+
|
|
+ protected static void log(String s) {
|
|
+ if (verbose) {
|
|
+ log(Level.INFO, s);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ protected static void log(Level level, String s) {
|
|
+ Bukkit.getLogger().log(level, s);
|
|
+ }
|
|
+
|
|
+ public static void registerCommands() {
|
|
+ for (Map.Entry<String, Command> entry : commands.entrySet()) {
|
|
+ MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Purpur", entry.getValue());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ static void readConfig(Class<?> clazz, Object instance) {
|
|
+ for (Method method : clazz.getDeclaredMethods()) {
|
|
+ if (Modifier.isPrivate(method.getModifiers())) {
|
|
+ if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
|
|
+ try {
|
|
+ method.setAccessible(true);
|
|
+ method.invoke(instance);
|
|
+ } catch (InvocationTargetException ex) {
|
|
+ throw Throwables.propagate(ex.getCause());
|
|
+ } catch (Exception ex) {
|
|
+ Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ config.save(CONFIG_FILE);
|
|
+ } catch (IOException ex) {
|
|
+ Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static void set(String path, Object val) {
|
|
+ config.addDefault(path, val);
|
|
+ config.set(path, val);
|
|
+ }
|
|
+
|
|
+ private static String getString(String path, String def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getString(path, config.getString(path));
|
|
+ }
|
|
+
|
|
+ private static boolean getBoolean(String path, boolean def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getBoolean(path, config.getBoolean(path));
|
|
+ }
|
|
+
|
|
+ private static double getDouble(String path, double def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getDouble(path, config.getDouble(path));
|
|
+ }
|
|
+
|
|
+ private static int getInt(String path, int def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getInt(path, config.getInt(path));
|
|
+ }
|
|
+
|
|
+ private static <T> List getList(String path, T def) {
|
|
+ config.addDefault(path, def);
|
|
+ return config.getList(path, config.getList(path));
|
|
+ }
|
|
+
|
|
+ static Map<String, Object> getMap(String path, Map<String, Object> def) {
|
|
+ if (def != null && config.getConfigurationSection(path) == null) {
|
|
+ config.addDefault(path, def);
|
|
+ return def;
|
|
+ }
|
|
+ return toMap(config.getConfigurationSection(path));
|
|
+ }
|
|
+
|
|
+ private static Map<String, Object> toMap(ConfigurationSection section) {
|
|
+ ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
|
|
+ if (section != null) {
|
|
+ for (String key : section.getKeys(false)) {
|
|
+ Object obj = section.get(key);
|
|
+ if (obj != null) {
|
|
+ builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return builder.build();
|
|
+ }
|
|
+
|
|
+ public static String cannotRideMob = "<red>You cannot mount that mob";
|
|
+ public static String afkBroadcastAway = "<yellow><italic>%s is now AFK";
|
|
+ public static String afkBroadcastBack = "<yellow><italic>%s is no longer AFK";
|
|
+ public static boolean afkBroadcastUseDisplayName = false;
|
|
+ public static String afkTabListPrefix = "[AFK] ";
|
|
+ public static String afkTabListSuffix = "";
|
|
+ public static String creditsCommandOutput = "<green>%s has been shown the end credits";
|
|
+ public static String demoCommandOutput = "<green>%s has been shown the demo screen";
|
|
+ public static String pingCommandOutput = "<green>%s's ping is %sms";
|
|
+ public static String ramCommandOutput = "<green>Ram Usage: <used>/<xmx> (<percent>)";
|
|
+ public static String rambarCommandOutput = "<green>Rambar toggled <onoff> for <target>";
|
|
+ public static String tpsbarCommandOutput = "<green>Tpsbar toggled <onoff> for <target>";
|
|
+ public static String dontRunWithScissors = "<red><italic>Don't run with scissors!";
|
|
+ public static String uptimeCommandOutput = "<green>Server uptime is <uptime>";
|
|
+ public static String unverifiedUsername = "default";
|
|
+ public static String sleepSkippingNight = "default";
|
|
+ public static String sleepingPlayersPercent = "default";
|
|
+ private static void messages() {
|
|
+ cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob);
|
|
+ afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway);
|
|
+ afkBroadcastBack = getString("settings.messages.afk-broadcast-back", afkBroadcastBack);
|
|
+ afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName);
|
|
+ afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix)));
|
|
+ afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix)));
|
|
+ creditsCommandOutput = getString("settings.messages.credits-command-output", creditsCommandOutput);
|
|
+ demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput);
|
|
+ pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput);
|
|
+ ramCommandOutput = getString("settings.messages.ram-command-output", ramCommandOutput);
|
|
+ rambarCommandOutput = getString("settings.messages.rambar-command-output", rambarCommandOutput);
|
|
+ tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput);
|
|
+ dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors);
|
|
+ uptimeCommandOutput = getString("settings.messages.uptime-command-output", uptimeCommandOutput);
|
|
+ unverifiedUsername = getString("settings.messages.unverified-username", unverifiedUsername);
|
|
+ sleepSkippingNight = getString("settings.messages.sleep-skipping-night", sleepSkippingNight);
|
|
+ sleepingPlayersPercent = getString("settings.messages.sleeping-players-percent", sleepingPlayersPercent);
|
|
+ }
|
|
+
|
|
+ public static String deathMsgRunWithScissors = "<player> slipped and fell on their shears";
|
|
+ public static String deathMsgStonecutter = "<player> has sawed themself in half";
|
|
+ private static void deathMessages() {
|
|
+ deathMsgRunWithScissors = getString("settings.messages.death-message.run-with-scissors", deathMsgRunWithScissors);
|
|
+ deathMsgStonecutter = getString("settings.messages.death-message.stonecutter", deathMsgStonecutter);
|
|
+ }
|
|
+
|
|
+ public static boolean advancementOnlyBroadcastToAffectedPlayer = false;
|
|
+ public static boolean deathMessageOnlyBroadcastToAffectedPlayer = false;
|
|
+ private static void broadcastSettings() {
|
|
+ if (version < 13) {
|
|
+ boolean oldValue = getBoolean("settings.advancement.only-broadcast-to-affected-player", false);
|
|
+ set("settings.broadcasts.advancement.only-broadcast-to-affected-player", oldValue);
|
|
+ set("settings.advancement.only-broadcast-to-affected-player", null);
|
|
+ }
|
|
+ advancementOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.advancement.only-broadcast-to-affected-player", advancementOnlyBroadcastToAffectedPlayer);
|
|
+ deathMessageOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.death.only-broadcast-to-affected-player", deathMessageOnlyBroadcastToAffectedPlayer);
|
|
+ }
|
|
+
|
|
+ public static String serverModName = "Purpur";
|
|
+ private static void serverModName() {
|
|
+ serverModName = getString("settings.server-mod-name", serverModName);
|
|
+ }
|
|
+
|
|
+ public static double laggingThreshold = 19.0D;
|
|
+ private static void tickLoopSettings() {
|
|
+ laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold);
|
|
+ }
|
|
+
|
|
+ public static boolean useAlternateKeepAlive = false;
|
|
+ private static void useAlternateKeepAlive() {
|
|
+ useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive);
|
|
+ }
|
|
+
|
|
+ public static boolean disableGiveCommandDrops = false;
|
|
+ private static void disableGiveCommandDrops() {
|
|
+ disableGiveCommandDrops = getBoolean("settings.disable-give-dropping", disableGiveCommandDrops);
|
|
+ }
|
|
+
|
|
+ public static String commandRamBarTitle = "<gray>Ram<yellow>:</yellow> <used>/<xmx> (<percent>)";
|
|
+ public static BossBar.Overlay commandRamBarProgressOverlay = BossBar.Overlay.NOTCHED_20;
|
|
+ public static BossBar.Color commandRamBarProgressColorGood = BossBar.Color.GREEN;
|
|
+ public static BossBar.Color commandRamBarProgressColorMedium = BossBar.Color.YELLOW;
|
|
+ public static BossBar.Color commandRamBarProgressColorLow = BossBar.Color.RED;
|
|
+ public static String commandRamBarTextColorGood = "<gradient:#55ff55:#00aa00><text></gradient>";
|
|
+ public static String commandRamBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
|
|
+ public static String commandRamBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
|
|
+ public static int commandRamBarTickInterval = 20;
|
|
+ public static String commandTPSBarTitle = "<gray>TPS<yellow>:</yellow> <tps> MSPT<yellow>:</yellow> <mspt> Ping<yellow>:</yellow> <ping>ms";
|
|
+ public static BossBar.Overlay commandTPSBarProgressOverlay = BossBar.Overlay.NOTCHED_20;
|
|
+ public static TPSBarTask.FillMode commandTPSBarProgressFillMode = TPSBarTask.FillMode.MSPT;
|
|
+ public static BossBar.Color commandTPSBarProgressColorGood = BossBar.Color.GREEN;
|
|
+ public static BossBar.Color commandTPSBarProgressColorMedium = BossBar.Color.YELLOW;
|
|
+ public static BossBar.Color commandTPSBarProgressColorLow = BossBar.Color.RED;
|
|
+ public static String commandTPSBarTextColorGood = "<gradient:#55ff55:#00aa00><text></gradient>";
|
|
+ public static String commandTPSBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
|
|
+ public static String commandTPSBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
|
|
+ public static int commandTPSBarTickInterval = 20;
|
|
+ public static String commandCompassBarTitle = "S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 ";
|
|
+ public static BossBar.Overlay commandCompassBarProgressOverlay = BossBar.Overlay.PROGRESS;
|
|
+ public static BossBar.Color commandCompassBarProgressColor = BossBar.Color.BLUE;
|
|
+ public static float commandCompassBarProgressPercent = 1.0F;
|
|
+ public static int commandCompassBarTickInterval = 5;
|
|
+ public static boolean commandGamemodeRequiresPermission = false;
|
|
+ public static boolean hideHiddenPlayersFromEntitySelector = false;
|
|
+ public static String uptimeFormat = "<days><hours><minutes><seconds>";
|
|
+ public static String uptimeDay = "%02d day, ";
|
|
+ public static String uptimeDays = "%02d days, ";
|
|
+ public static String uptimeHour = "%02d hour, ";
|
|
+ public static String uptimeHours = "%02d hours, ";
|
|
+ public static String uptimeMinute = "%02d minute, and ";
|
|
+ public static String uptimeMinutes = "%02d minutes, and ";
|
|
+ public static String uptimeSecond = "%02d second";
|
|
+ public static String uptimeSeconds = "%02d seconds";
|
|
+ private static void commandSettings() {
|
|
+ commandRamBarTitle = getString("settings.command.rambar.title", commandRamBarTitle);
|
|
+ commandRamBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.rambar.overlay", commandRamBarProgressOverlay.name()));
|
|
+ commandRamBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.good", commandRamBarProgressColorGood.name()));
|
|
+ commandRamBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.medium", commandRamBarProgressColorMedium.name()));
|
|
+ commandRamBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.low", commandRamBarProgressColorLow.name()));
|
|
+ commandRamBarTextColorGood = getString("settings.command.rambar.text-color.good", commandRamBarTextColorGood);
|
|
+ commandRamBarTextColorMedium = getString("settings.command.rambar.text-color.medium", commandRamBarTextColorMedium);
|
|
+ commandRamBarTextColorLow = getString("settings.command.rambar.text-color.low", commandRamBarTextColorLow);
|
|
+ commandRamBarTickInterval = getInt("settings.command.rambar.tick-interval", commandRamBarTickInterval);
|
|
+
|
|
+ commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle);
|
|
+ commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name()));
|
|
+ commandTPSBarProgressFillMode = TPSBarTask.FillMode.valueOf(getString("settings.command.tpsbar.fill-mode", commandTPSBarProgressFillMode.name()));
|
|
+ commandTPSBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.good", commandTPSBarProgressColorGood.name()));
|
|
+ commandTPSBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.medium", commandTPSBarProgressColorMedium.name()));
|
|
+ commandTPSBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.low", commandTPSBarProgressColorLow.name()));
|
|
+ commandTPSBarTextColorGood = getString("settings.command.tpsbar.text-color.good", commandTPSBarTextColorGood);
|
|
+ commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium);
|
|
+ commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow);
|
|
+ commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval);
|
|
+
|
|
+ commandCompassBarTitle = getString("settings.command.compass.title", commandCompassBarTitle);
|
|
+ commandCompassBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.compass.overlay", commandCompassBarProgressOverlay.name()));
|
|
+ commandCompassBarProgressColor = BossBar.Color.valueOf(getString("settings.command.compass.progress-color", commandCompassBarProgressColor.name()));
|
|
+ commandCompassBarProgressPercent = (float) getDouble("settings.command.compass.percent", commandCompassBarProgressPercent);
|
|
+ commandCompassBarTickInterval = getInt("settings.command.compass.tick-interval", commandCompassBarTickInterval);
|
|
+
|
|
+ commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission);
|
|
+ hideHiddenPlayersFromEntitySelector = getBoolean("settings.command.hide-hidden-players-from-entity-selector", hideHiddenPlayersFromEntitySelector);
|
|
+ uptimeFormat = getString("settings.command.uptime.format", uptimeFormat);
|
|
+ uptimeDay = getString("settings.command.uptime.day", uptimeDay);
|
|
+ uptimeDays = getString("settings.command.uptime.days", uptimeDays);
|
|
+ uptimeHour = getString("settings.command.uptime.hour", uptimeHour);
|
|
+ uptimeHours = getString("settings.command.uptime.hours", uptimeHours);
|
|
+ uptimeMinute = getString("settings.command.uptime.minute", uptimeMinute);
|
|
+ uptimeMinutes = getString("settings.command.uptime.minutes", uptimeMinutes);
|
|
+ uptimeSecond = getString("settings.command.uptime.second", uptimeSecond);
|
|
+ uptimeSeconds = getString("settings.command.uptime.seconds", uptimeSeconds);
|
|
+ }
|
|
+
|
|
+ public static int barrelRows = 3;
|
|
+ public static boolean enderChestSixRows = false;
|
|
+ public static boolean enderChestPermissionRows = false;
|
|
+ public static boolean cryingObsidianValidForPortalFrame = false;
|
|
+ public static int beeInsideBeeHive = 3;
|
|
+ public static boolean anvilCumulativeCost = true;
|
|
+ public static int lightningRodRange = 128;
|
|
+ public static Set<Enchantment> grindstoneIgnoredEnchants = new HashSet<>();
|
|
+ public static boolean grindstoneRemoveAttributes = false;
|
|
+ public static boolean grindstoneRemoveDisplay = false;
|
|
+ public static int caveVinesMaxGrowthAge = 25;
|
|
+ public static int kelpMaxGrowthAge = 25;
|
|
+ public static int twistingVinesMaxGrowthAge = 25;
|
|
+ public static int weepingVinesMaxGrowthAge = 25;
|
|
+ private static void blockSettings() {
|
|
+ if (version < 3) {
|
|
+ boolean oldValue = getBoolean("settings.barrel.packed-barrels", true);
|
|
+ set("settings.blocks.barrel.six-rows", oldValue);
|
|
+ set("settings.packed-barrels", null);
|
|
+ oldValue = getBoolean("settings.large-ender-chests", true);
|
|
+ set("settings.blocks.ender_chest.six-rows", oldValue);
|
|
+ set("settings.large-ender-chests", null);
|
|
+ }
|
|
+ if (version < 20) {
|
|
+ boolean oldValue = getBoolean("settings.blocks.barrel.six-rows", false);
|
|
+ set("settings.blocks.barrel.rows", oldValue ? 6 : 3);
|
|
+ set("settings.blocks.barrel.six-rows", null);
|
|
+ }
|
|
+ barrelRows = getInt("settings.blocks.barrel.rows", barrelRows);
|
|
+ if (barrelRows < 1 || barrelRows > 6) {
|
|
+ Bukkit.getLogger().severe("settings.blocks.barrel.rows must be 1-6, resetting to default");
|
|
+ barrelRows = 3;
|
|
+ }
|
|
+ org.bukkit.event.inventory.InventoryType.BARREL.setDefaultSize(switch (barrelRows) {
|
|
+ case 6 -> 54;
|
|
+ case 5 -> 45;
|
|
+ case 4 -> 36;
|
|
+ case 2 -> 18;
|
|
+ case 1 -> 9;
|
|
+ default -> 27;
|
|
+ });
|
|
+ enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows);
|
|
+ org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27);
|
|
+ enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows);
|
|
+ cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame);
|
|
+ beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive);
|
|
+ anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost);
|
|
+ lightningRodRange = getInt("settings.blocks.lightning_rod.range", lightningRodRange);
|
|
+ ArrayList<String> defaultCurses = new ArrayList<>(){{
|
|
+ add("minecraft:binding_curse");
|
|
+ add("minecraft:vanishing_curse");
|
|
+ }};
|
|
+ if (version < 24 && !getBoolean("settings.blocks.grindstone.ignore-curses", true)) {
|
|
+ defaultCurses.clear();
|
|
+ }
|
|
+ getList("settings.blocks.grindstone.ignored-enchants", defaultCurses).forEach(key -> {
|
|
+ Enchantment enchantment = BuiltInRegistries.ENCHANTMENT.get(new ResourceLocation(key.toString()));
|
|
+ grindstoneIgnoredEnchants.add(enchantment);
|
|
+ });
|
|
+ grindstoneRemoveAttributes = getBoolean("settings.blocks.grindstone.remove-attributes", grindstoneRemoveAttributes);
|
|
+ grindstoneRemoveDisplay = getBoolean("settings.blocks.grindstone.remove-name-and-lore", grindstoneRemoveDisplay);
|
|
+ caveVinesMaxGrowthAge = getInt("settings.blocks.cave_vines.max-growth-age", caveVinesMaxGrowthAge);
|
|
+ if (caveVinesMaxGrowthAge > 25) {
|
|
+ caveVinesMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.cave_vines.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ kelpMaxGrowthAge = getInt("settings.blocks.kelp.max-growth-age", kelpMaxGrowthAge);
|
|
+ if (kelpMaxGrowthAge > 25) {
|
|
+ kelpMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.kelp.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ twistingVinesMaxGrowthAge = getInt("settings.blocks.twisting_vines.max-growth-age", twistingVinesMaxGrowthAge);
|
|
+ if (twistingVinesMaxGrowthAge > 25) {
|
|
+ twistingVinesMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.twisting_vines.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ weepingVinesMaxGrowthAge = getInt("settings.blocks.weeping_vines.max-growth-age", weepingVinesMaxGrowthAge);
|
|
+ if (weepingVinesMaxGrowthAge > 25) {
|
|
+ weepingVinesMaxGrowthAge = 25;
|
|
+ log(Level.WARNING, "blocks.weeping_vines.max-growth-age is set to above maximum allowed value of 25");
|
|
+ log(Level.WARNING, "Using value of 25 to prevent issues");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static boolean allowInfinityMending = false;
|
|
+ public static boolean allowCrossbowInfinity = false;
|
|
+ public static boolean allowShearsLooting = false;
|
|
+ public static boolean allowTransparentBlocksInEnchantmentBox = false;
|
|
+ public static boolean allowUnsafeEnchants = false;
|
|
+ public static boolean allowInapplicableEnchants = true;
|
|
+ public static boolean allowIncompatibleEnchants = true;
|
|
+ public static boolean allowHigherEnchantsLevels = true;
|
|
+ public static boolean allowUnsafeEnchantCommand = false;
|
|
+ public static boolean clampEnchantLevels = true;
|
|
+ private static void enchantmentSettings() {
|
|
+ if (version < 5) {
|
|
+ boolean oldValue = getBoolean("settings.enchantment.allow-infinite-and-mending-together", false);
|
|
+ set("settings.enchantment.allow-infinity-and-mending-together", oldValue);
|
|
+ set("settings.enchantment.allow-infinite-and-mending-together", null);
|
|
+ }
|
|
+ if (version < 30) {
|
|
+ boolean oldValue = getBoolean("settings.enchantment.allow-unsafe-enchants", false);
|
|
+ set("settings.enchantment.anvil.allow-unsafe-enchants", oldValue);
|
|
+ set("settings.enchantment.anvil.allow-inapplicable-enchants", true);
|
|
+ set("settings.enchantment.anvil.allow-incompatible-enchants", true);
|
|
+ set("settings.enchantment.anvil.allow-higher-enchants-levels", true);
|
|
+ set("settings.enchantment.allow-unsafe-enchants", null);
|
|
+ }
|
|
+ allowInfinityMending = getBoolean("settings.enchantment.allow-infinity-and-mending-together", allowInfinityMending);
|
|
+ allowCrossbowInfinity = getBoolean("settings.enchantment.allow-infinity-on-crossbow", allowCrossbowInfinity);
|
|
+ allowShearsLooting = getBoolean("settings.enchantment.allow-looting-on-shears", allowShearsLooting);
|
|
+ allowTransparentBlocksInEnchantmentBox = getBoolean("settings.enchantment.allow-transparent-blocks-in-enchantment-box", allowTransparentBlocksInEnchantmentBox);
|
|
+ allowUnsafeEnchants = getBoolean("settings.enchantment.anvil.allow-unsafe-enchants", allowUnsafeEnchants);
|
|
+ allowInapplicableEnchants = getBoolean("settings.enchantment.anvil.allow-inapplicable-enchants", allowInapplicableEnchants);
|
|
+ allowIncompatibleEnchants = getBoolean("settings.enchantment.anvil.allow-incompatible-enchants", allowIncompatibleEnchants);
|
|
+ allowHigherEnchantsLevels = getBoolean("settings.enchantment.anvil.allow-higher-enchants-levels", allowHigherEnchantsLevels);
|
|
+ allowUnsafeEnchantCommand = getBoolean("settings.enchantment.allow-unsafe-enchant-command", allowUnsafeEnchants); // allowUnsafeEnchants as default for backwards compatability
|
|
+ clampEnchantLevels = getBoolean("settings.enchantment.clamp-levels", clampEnchantLevels);
|
|
+ }
|
|
+
|
|
+ public static boolean endermanShortHeight = false;
|
|
+ private static void entitySettings() {
|
|
+ endermanShortHeight = getBoolean("settings.entity.enderman.short-height", endermanShortHeight);
|
|
+ if (endermanShortHeight) EntityType.ENDERMAN.setDimensions(EntityDimensions.scalable(0.6F, 1.9F));
|
|
+ }
|
|
+
|
|
+ public static boolean allowWaterPlacementInTheEnd = true;
|
|
+ private static void allowWaterPlacementInEnd() {
|
|
+ allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd);
|
|
+ }
|
|
+
|
|
+ public static boolean disableMushroomBlockUpdates = false;
|
|
+ public static boolean disableNoteBlockUpdates = false;
|
|
+ public static boolean disableChorusPlantUpdates = false;
|
|
+ private static void blockUpdatesSettings() {
|
|
+ disableMushroomBlockUpdates = getBoolean("settings.blocks.disable-mushroom-updates", disableMushroomBlockUpdates);
|
|
+ disableNoteBlockUpdates = getBoolean("settings.blocks.disable-note-block-updates", disableNoteBlockUpdates);
|
|
+ disableChorusPlantUpdates = getBoolean("settings.blocks.disable-chorus-plant-updates", disableChorusPlantUpdates);
|
|
+ }
|
|
+
|
|
+ public static boolean loggerSuppressInitLegacyMaterialError = false;
|
|
+ public static boolean loggerSuppressIgnoredAdvancementWarnings = false;
|
|
+ public static boolean loggerSuppressUnrecognizedRecipeErrors = false;
|
|
+ public static boolean loggerSuppressSetBlockFarChunk = false;
|
|
+ public static boolean loggerSuppressLibraryLoader = false;
|
|
+ private static void loggerSettings() {
|
|
+ loggerSuppressInitLegacyMaterialError = getBoolean("settings.logger.suppress-init-legacy-material-errors", loggerSuppressInitLegacyMaterialError);
|
|
+ loggerSuppressIgnoredAdvancementWarnings = getBoolean("settings.logger.suppress-ignored-advancement-warnings", loggerSuppressIgnoredAdvancementWarnings);
|
|
+ loggerSuppressUnrecognizedRecipeErrors = getBoolean("settings.logger.suppress-unrecognized-recipe-errors", loggerSuppressUnrecognizedRecipeErrors);
|
|
+ loggerSuppressSetBlockFarChunk = getBoolean("settings.logger.suppress-setblock-in-far-chunk-errors", loggerSuppressSetBlockFarChunk);
|
|
+ loggerSuppressLibraryLoader = getBoolean("settings.logger.suppress-library-loader", loggerSuppressLibraryLoader);
|
|
+ org.bukkit.plugin.java.JavaPluginLoader.SuppressLibraryLoaderLogger = loggerSuppressLibraryLoader;
|
|
+ }
|
|
+
|
|
+ public static boolean tpsCatchup = true;
|
|
+ private static void tpsCatchup() {
|
|
+ tpsCatchup = getBoolean("settings.tps-catchup", tpsCatchup);
|
|
+ }
|
|
+
|
|
+ public static boolean useUPnP = false;
|
|
+ public static boolean maxJoinsPerSecond = false;
|
|
+ public static boolean kickForOutOfOrderChat = true;
|
|
+ private static void networkSettings() {
|
|
+ useUPnP = getBoolean("settings.network.upnp-port-forwarding", useUPnP);
|
|
+ maxJoinsPerSecond = getBoolean("settings.network.max-joins-per-second", maxJoinsPerSecond);
|
|
+ kickForOutOfOrderChat = getBoolean("settings.network.kick-for-out-of-order-chat", kickForOutOfOrderChat);
|
|
+ }
|
|
+
|
|
+ public static java.util.regex.Pattern usernameValidCharactersPattern;
|
|
+ private static void usernameValidationSettings() {
|
|
+ String defaultPattern = "^[a-zA-Z0-9_.]*$";
|
|
+ String setPattern = getString("settings.username-valid-characters", defaultPattern);
|
|
+ usernameValidCharactersPattern = java.util.regex.Pattern.compile(setPattern == null || setPattern.isBlank() ? defaultPattern : setPattern);
|
|
+ }
|
|
+
|
|
+ private static void foodSettings() {
|
|
+ ConfigurationSection properties = config.getConfigurationSection("settings.food-properties");
|
|
+ if (properties == null) {
|
|
+ config.addDefault("settings.food-properties", new HashMap<>());
|
|
+ return;
|
|
+ }
|
|
+ properties.getKeys(false).forEach(foodKey -> {
|
|
+ FoodProperties food = Foods.ALL_PROPERTIES.get(foodKey);
|
|
+ if (food == null) {
|
|
+ PurpurConfig.log(Level.SEVERE, "Invalid food property: " + foodKey);
|
|
+ return;
|
|
+ }
|
|
+ FoodProperties foodDefaults = Foods.DEFAULT_PROPERTIES.get(foodKey);
|
|
+ food.setNutrition(properties.getInt(foodKey + ".nutrition", foodDefaults.getNutrition()));
|
|
+ food.setSaturationModifier((float) properties.getDouble(foodKey + ".saturation-modifier", foodDefaults.getSaturationModifier()));
|
|
+ food.setIsMeat(properties.getBoolean(foodKey + ".is-meat", foodDefaults.isMeat()));
|
|
+ food.setCanAlwaysEat(properties.getBoolean(foodKey + ".can-always-eat", foodDefaults.canAlwaysEat()));
|
|
+ food.setFastFood(properties.getBoolean(foodKey + ".fast-food", foodDefaults.isFastFood()));
|
|
+ ConfigurationSection effects = properties.getConfigurationSection(foodKey + ".effects");
|
|
+ if (effects != null) {
|
|
+ Map<String, Object> effectDefaults = new HashMap<>();
|
|
+ foodDefaults.getEffects().forEach(pair -> {
|
|
+ effectDefaults.put("chance", pair.getSecond());
|
|
+ MobEffectInstance effect = pair.getFirst();
|
|
+ effectDefaults.put("duration", effect.getDuration());
|
|
+ effectDefaults.put("amplifier", effect.getAmplifier());
|
|
+ effectDefaults.put("ambient", effect.isAmbient());
|
|
+ effectDefaults.put("visible", effect.isVisible());
|
|
+ effectDefaults.put("show-icon", effect.showIcon());
|
|
+ });
|
|
+ effects.getKeys(false).forEach(effectKey -> {
|
|
+ MobEffect effect = BuiltInRegistries.MOB_EFFECT.get(new ResourceLocation(effectKey));
|
|
+ if (effect == null) {
|
|
+ PurpurConfig.log(Level.SEVERE, "Invalid food property effect for " + foodKey + ": " + effectKey);
|
|
+ return;
|
|
+ }
|
|
+ food.getEffects().removeIf(pair -> pair.getFirst().getEffect() == effect);
|
|
+ float chance = (float) effects.getDouble(effectKey + ".chance", ((Float) effectDefaults.get("chance")).doubleValue());
|
|
+ int duration = effects.getInt(effectKey + ".duration", (int) effectDefaults.get("duration"));
|
|
+ if (chance <= 0.0F || duration < 0) {
|
|
+ return;
|
|
+ }
|
|
+ int amplifier = effects.getInt(effectKey + ".amplifier", (int) effectDefaults.get("amplifier"));
|
|
+ boolean ambient = effects.getBoolean(effectKey + ".ambient", (boolean) effectDefaults.get("ambient"));
|
|
+ boolean visible = effects.getBoolean(effectKey + ".visible", (boolean) effectDefaults.get("visible"));
|
|
+ boolean showIcon = effects.getBoolean(effectKey + ".show-icon", (boolean) effectDefaults.get("show-icon"));
|
|
+ food.getEffects().add(Pair.of(new MobEffectInstance(effect, duration, amplifier, ambient, visible, showIcon), chance));
|
|
+ });
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public static boolean fixNetworkSerializedItemsInCreative = false;
|
|
+ private static void fixNetworkSerializedCreativeItems() {
|
|
+ fixNetworkSerializedItemsInCreative = getBoolean("settings.fix-network-serialized-items-in-creative", fixNetworkSerializedItemsInCreative);
|
|
+ }
|
|
+
|
|
+ public static boolean fixProjectileLootingTransfer = false;
|
|
+ private static void fixProjectileLootingTransfer() {
|
|
+ fixProjectileLootingTransfer = getBoolean("settings.fix-projectile-looting-transfer", fixProjectileLootingTransfer);
|
|
+ }
|
|
+
|
|
+ public static boolean clampAttributes = true;
|
|
+ private static void clampAttributes() {
|
|
+ clampAttributes = getBoolean("settings.clamp-attributes", clampAttributes);
|
|
+ }
|
|
+
|
|
+ public static boolean limitArmor = true;
|
|
+ private static void limitArmor() {
|
|
+ limitArmor = getBoolean("settings.limit-armor", limitArmor);
|
|
+ }
|
|
+
|
|
+ private static void blastResistanceSettings() {
|
|
+ getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId));
|
|
+ if (block == Blocks.AIR) {
|
|
+ log(Level.SEVERE, "Invalid block for `settings.blast-resistance-overrides`: " + blockId);
|
|
+ return;
|
|
+ }
|
|
+ if (!(value instanceof Number blastResistance)) {
|
|
+ log(Level.SEVERE, "Invalid blast resistance for `settings.blast-resistance-overrides." + blockId + "`: " + value);
|
|
+ return;
|
|
+ }
|
|
+ block.explosionResistance = blastResistance.floatValue();
|
|
+ });
|
|
+ }
|
|
+ private static void blockFallMultiplierSettings() {
|
|
+ getMap("settings.block-fall-multipliers", Map.ofEntries(
|
|
+ Map.entry("minecraft:hay_block", Map.of("damage", 0.2F)),
|
|
+ Map.entry("minecraft:white_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:light_gray_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:gray_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:black_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:brown_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:pink_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:red_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:orange_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:yellow_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:green_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:lime_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:cyan_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:light_blue_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:blue_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:purple_bed", Map.of("distance", 0.5F)),
|
|
+ Map.entry("minecraft:magenta_bed", Map.of("distance", 0.5F))
|
|
+ )).forEach((blockId, value) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId));
|
|
+ if (block == Blocks.AIR) {
|
|
+ log(Level.SEVERE, "Invalid block for `settings.block-fall-multipliers`: " + blockId);
|
|
+ return;
|
|
+ }
|
|
+ if (!(value instanceof Map<?, ?> map)) {
|
|
+ log(Level.SEVERE, "Invalid fall multiplier for `settings.block-fall-multipliers." + blockId + "`: " + value
|
|
+ + ", expected a map with keys `damage` and `distance` to floats.");
|
|
+ return;
|
|
+ }
|
|
+ Object rawFallDamageMultiplier = map.get("damage");
|
|
+ if (rawFallDamageMultiplier == null) rawFallDamageMultiplier = 1F;
|
|
+ if (!(rawFallDamageMultiplier instanceof Number fallDamageMultiplier)) {
|
|
+ log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".damage`: " + map.get("damage"));
|
|
+ return;
|
|
+ }
|
|
+ Object rawFallDistanceMultiplier = map.get("distance");
|
|
+ if (rawFallDistanceMultiplier == null) rawFallDistanceMultiplier = 1F;
|
|
+ if (!(rawFallDistanceMultiplier instanceof Number fallDistanceMultiplier)) {
|
|
+ log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".distance`: " + map.get("distance"));
|
|
+ return;
|
|
+ }
|
|
+ block.fallDamageMultiplier = fallDamageMultiplier.floatValue();
|
|
+ block.fallDistanceMultiplier = fallDistanceMultiplier.floatValue();
|
|
+ });
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..397c4afa8da85845f49974832674a6e45ee6edb7
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
|
|
@@ -0,0 +1,3203 @@
|
|
+package org.purpurmc.purpur;
|
|
+
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.item.DyeColor;
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.item.Items;
|
|
+import net.minecraft.world.level.Explosion;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.state.properties.Tilt;
|
|
+import org.purpurmc.purpur.entity.GlowSquidColor;
|
|
+import org.purpurmc.purpur.tool.Strippable;
|
|
+import org.purpurmc.purpur.tool.Tillable;
|
|
+import org.purpurmc.purpur.tool.Waxable;
|
|
+import org.purpurmc.purpur.tool.Weatherable;
|
|
+import org.apache.commons.lang.BooleanUtils;
|
|
+import org.bukkit.ChatColor;
|
|
+import org.bukkit.World;
|
|
+import org.bukkit.configuration.ConfigurationSection;
|
|
+import java.util.ArrayList;
|
|
+import java.util.HashMap;
|
|
+import java.util.List;
|
|
+import java.util.Locale;
|
|
+import java.util.Map;
|
|
+import java.util.function.Predicate;
|
|
+import java.util.logging.Level;
|
|
+import static org.purpurmc.purpur.PurpurConfig.log;
|
|
+
|
|
+@SuppressWarnings("unused")
|
|
+public class PurpurWorldConfig {
|
|
+
|
|
+ private final String worldName;
|
|
+ private final World.Environment environment;
|
|
+
|
|
+ public PurpurWorldConfig(String worldName, World.Environment environment) {
|
|
+ this.worldName = worldName;
|
|
+ this.environment = environment;
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ public void init() {
|
|
+ log("-------- World Settings For [" + worldName + "] --------");
|
|
+ PurpurConfig.readConfig(PurpurWorldConfig.class, this);
|
|
+ }
|
|
+
|
|
+ private void set(String path, Object val) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, val);
|
|
+ PurpurConfig.config.set("world-settings.default." + path, val);
|
|
+ if (PurpurConfig.config.get("world-settings." + worldName + "." + path) != null) {
|
|
+ PurpurConfig.config.addDefault("world-settings." + worldName + "." + path, val);
|
|
+ PurpurConfig.config.set("world-settings." + worldName + "." + path, val);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private ConfigurationSection getConfigurationSection(String path) {
|
|
+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
|
|
+ return section != null ? section : PurpurConfig.config.getConfigurationSection("world-settings.default." + path);
|
|
+ }
|
|
+
|
|
+ private String getString(String path, String def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getString("world-settings." + worldName + "." + path, PurpurConfig.config.getString("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private boolean getBoolean(String path, boolean def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private boolean getBoolean(String path, Predicate<Boolean> predicate) {
|
|
+ String val = getString(path, "default").toLowerCase();
|
|
+ Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default");
|
|
+ return predicate.test(bool);
|
|
+ }
|
|
+
|
|
+ private double getDouble(String path, double def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private int getInt(String path, int def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getInt("world-settings." + worldName + "." + path, PurpurConfig.config.getInt("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private <T> List<?> getList(String path, T def) {
|
|
+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
|
|
+ return PurpurConfig.config.getList("world-settings." + worldName + "." + path, PurpurConfig.config.getList("world-settings.default." + path));
|
|
+ }
|
|
+
|
|
+ private Map<String, Object> getMap(String path, Map<String, Object> def) {
|
|
+ final Map<String, Object> fallback = PurpurConfig.getMap("world-settings.default." + path, def);
|
|
+ final Map<String, Object> value = PurpurConfig.getMap("world-settings." + worldName + "." + path, null);
|
|
+ return value.isEmpty() ? fallback : value;
|
|
+ }
|
|
+
|
|
+ public float armorstandStepHeight = 0.0F;
|
|
+ public boolean armorstandSetNameVisible = true;
|
|
+ public boolean armorstandFixNametags = false;
|
|
+ public boolean armorstandMovement = true;
|
|
+ public boolean armorstandWaterMovement = true;
|
|
+ public boolean armorstandWaterFence = true;
|
|
+ public boolean armorstandPlaceWithArms = false;
|
|
+ private void armorstandSettings() {
|
|
+ armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight);
|
|
+ armorstandSetNameVisible = getBoolean("gameplay-mechanics.armorstand.set-name-visible-when-placing-with-custom-name", armorstandSetNameVisible);
|
|
+ armorstandFixNametags = getBoolean("gameplay-mechanics.armorstand.fix-nametags", armorstandFixNametags);
|
|
+ armorstandMovement = getBoolean("gameplay-mechanics.armorstand.can-movement-tick", armorstandMovement);
|
|
+ armorstandWaterMovement = getBoolean("gameplay-mechanics.armorstand.can-move-in-water", armorstandWaterMovement);
|
|
+ armorstandWaterFence = getBoolean("gameplay-mechanics.armorstand.can-move-in-water-over-fence", armorstandWaterFence);
|
|
+ armorstandPlaceWithArms = getBoolean("gameplay-mechanics.armorstand.place-with-arms-visible", armorstandPlaceWithArms);
|
|
+ }
|
|
+
|
|
+ public boolean arrowMovementResetsDespawnCounter = true;
|
|
+ private void arrowSettings() {
|
|
+ arrowMovementResetsDespawnCounter = getBoolean("gameplay-mechanics.arrow.movement-resets-despawn-counter", arrowMovementResetsDespawnCounter);
|
|
+ }
|
|
+
|
|
+ public boolean useBetterMending = false;
|
|
+ public double mendingMultiplier = 1.0;
|
|
+ public boolean alwaysTameInCreative = false;
|
|
+ public boolean boatEjectPlayersOnLand = false;
|
|
+ public boolean boatsDoFallDamage = false;
|
|
+ public boolean disableDropsOnCrammingDeath = false;
|
|
+ public boolean entitiesCanUsePortals = true;
|
|
+ public boolean entitiesPickUpLootBypassMobGriefing = false;
|
|
+ public boolean fireballsBypassMobGriefing = false;
|
|
+ public boolean imposeTeleportRestrictionsOnGateways = false;
|
|
+ public boolean milkCuresBadOmen = true;
|
|
+ public boolean milkClearsBeneficialEffects = true;
|
|
+ public boolean noteBlockIgnoreAbove = false;
|
|
+ public boolean persistentDroppableEntityDisplayNames = true;
|
|
+ public boolean persistentTileEntityDisplayNames = false;
|
|
+ public boolean projectilesBypassMobGriefing = false;
|
|
+ public boolean tickFluids = true;
|
|
+ public double mobsBlindnessMultiplier = 1;
|
|
+ public double tridentLoyaltyVoidReturnHeight = 0.0D;
|
|
+ public double voidDamageHeight = -64.0D;
|
|
+ public double voidDamageDealt = 4.0D;
|
|
+ public int raidCooldownSeconds = 0;
|
|
+ public int animalBreedingCooldownSeconds = 0;
|
|
+ public boolean mobsIgnoreRails = false;
|
|
+ public boolean rainStopsAfterSleep = true;
|
|
+ public boolean thunderStopsAfterSleep = true;
|
|
+ public int mobLastHurtByPlayerTime = 100;
|
|
+ private void miscGameplayMechanicsSettings() {
|
|
+ useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending);
|
|
+ mendingMultiplier = getDouble("gameplay-mechanics.mending-multiplier", mendingMultiplier);
|
|
+ alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative);
|
|
+ boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand);
|
|
+ boatsDoFallDamage = getBoolean("gameplay-mechanics.boat.do-fall-damage", boatsDoFallDamage);
|
|
+ disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath);
|
|
+ entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals);
|
|
+ entitiesPickUpLootBypassMobGriefing = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", entitiesPickUpLootBypassMobGriefing);
|
|
+ fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing);
|
|
+ imposeTeleportRestrictionsOnGateways = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-gateways", imposeTeleportRestrictionsOnGateways);
|
|
+ milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen);
|
|
+ milkClearsBeneficialEffects = getBoolean("gameplay-mechanics.milk-clears-beneficial-effects", milkClearsBeneficialEffects);
|
|
+ noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove);
|
|
+ persistentTileEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityDisplayNames);
|
|
+ persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames);
|
|
+ projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing);
|
|
+ tickFluids = getBoolean("gameplay-mechanics.tick-fluids", tickFluids);
|
|
+ mobsBlindnessMultiplier = getDouble("gameplay-mechanics.entity-blindness-multiplier", mobsBlindnessMultiplier);
|
|
+ tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight);
|
|
+ voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight);
|
|
+ voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt);
|
|
+ raidCooldownSeconds = getInt("gameplay-mechanics.raid-cooldown-seconds", raidCooldownSeconds);
|
|
+ animalBreedingCooldownSeconds = getInt("gameplay-mechanics.animal-breeding-cooldown-seconds", animalBreedingCooldownSeconds);
|
|
+ mobsIgnoreRails = getBoolean("gameplay-mechanics.mobs-ignore-rails", mobsIgnoreRails);
|
|
+ rainStopsAfterSleep = getBoolean("gameplay-mechanics.rain-stops-after-sleep", rainStopsAfterSleep);
|
|
+ thunderStopsAfterSleep = getBoolean("gameplay-mechanics.thunder-stops-after-sleep", thunderStopsAfterSleep);
|
|
+ mobLastHurtByPlayerTime = getInt("gameplay-mechanics.mob-last-hurt-by-player-time", mobLastHurtByPlayerTime);
|
|
+ }
|
|
+
|
|
+ public int daytimeTicks = 12000;
|
|
+ public int nighttimeTicks = 12000;
|
|
+ private void daytimeCycleSettings() {
|
|
+ daytimeTicks = getInt("gameplay-mechanics.daylight-cycle-ticks.daytime", daytimeTicks);
|
|
+ nighttimeTicks = getInt("gameplay-mechanics.daylight-cycle-ticks.nighttime", nighttimeTicks);
|
|
+ }
|
|
+
|
|
+ public int drowningAirTicks = 300;
|
|
+ public int drowningDamageInterval = 20;
|
|
+ public double damageFromDrowning = 2.0F;
|
|
+ private void drowningSettings() {
|
|
+ drowningAirTicks = getInt("gameplay-mechanics.drowning.air-ticks", drowningAirTicks);
|
|
+ drowningDamageInterval = getInt("gameplay-mechanics.drowning.ticks-per-damage", drowningDamageInterval);
|
|
+ damageFromDrowning = getDouble("gameplay-mechanics.drowning.damage-from-drowning", damageFromDrowning);
|
|
+ }
|
|
+
|
|
+ public int elytraDamagePerSecond = 1;
|
|
+ public double elytraDamageMultiplyBySpeed = 0;
|
|
+ public boolean elytraIgnoreUnbreaking = false;
|
|
+ public int elytraDamagePerFireworkBoost = 0;
|
|
+ public int elytraDamagePerTridentBoost = 0;
|
|
+ public boolean elytraKineticDamage = true;
|
|
+ private void elytraSettings() {
|
|
+ elytraDamagePerSecond = getInt("gameplay-mechanics.elytra.damage-per-second", elytraDamagePerSecond);
|
|
+ elytraDamageMultiplyBySpeed = getDouble("gameplay-mechanics.elytra.damage-multiplied-by-speed", elytraDamageMultiplyBySpeed);
|
|
+ elytraIgnoreUnbreaking = getBoolean("gameplay-mechanics.elytra.ignore-unbreaking", elytraIgnoreUnbreaking);
|
|
+ elytraDamagePerFireworkBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.firework", elytraDamagePerFireworkBoost);
|
|
+ elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost);
|
|
+ elytraKineticDamage = getBoolean("gameplay-mechanics.elytra.kinetic-damage", elytraKineticDamage);
|
|
+ }
|
|
+
|
|
+ public int entityLifeSpan = 0;
|
|
+ public float entityLeftHandedChance = 0.05f;
|
|
+ public boolean entitySharedRandom = true;
|
|
+ private void entitySettings() {
|
|
+ entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan);
|
|
+ entityLeftHandedChance = (float) getDouble("gameplay-mechanics.entity-left-handed-chance", entityLeftHandedChance);
|
|
+ entitySharedRandom = getBoolean("settings.entity.shared-random", entitySharedRandom);
|
|
+ }
|
|
+
|
|
+ public boolean explosionClampRadius = true;
|
|
+ private void explosionSettings() {
|
|
+ explosionClampRadius = getBoolean("gameplay-mechanics.clamp-explosion-radius", explosionClampRadius);
|
|
+ }
|
|
+
|
|
+ public boolean infinityWorksWithoutArrows = false;
|
|
+ public boolean infinityWorksWithNormalArrows = true;
|
|
+ public boolean infinityWorksWithSpectralArrows = false;
|
|
+ public boolean infinityWorksWithTippedArrows = false;
|
|
+ private void infinityArrowsSettings() {
|
|
+ infinityWorksWithoutArrows = getBoolean("gameplay-mechanics.infinity-bow.works-without-arrows", infinityWorksWithoutArrows);
|
|
+ infinityWorksWithNormalArrows = getBoolean("gameplay-mechanics.infinity-bow.normal-arrows", infinityWorksWithNormalArrows);
|
|
+ infinityWorksWithSpectralArrows = getBoolean("gameplay-mechanics.infinity-bow.spectral-arrows", infinityWorksWithSpectralArrows);
|
|
+ infinityWorksWithTippedArrows = getBoolean("gameplay-mechanics.infinity-bow.tipped-arrows", infinityWorksWithTippedArrows);
|
|
+ }
|
|
+
|
|
+ public List<Item> itemImmuneToCactus = new ArrayList<>();
|
|
+ public List<Item> itemImmuneToExplosion = new ArrayList<>();
|
|
+ public List<Item> itemImmuneToFire = new ArrayList<>();
|
|
+ public List<Item> itemImmuneToLightning = new ArrayList<>();
|
|
+ public boolean dontRunWithScissors = false;
|
|
+ public boolean ignoreScissorsInWater = false;
|
|
+ public boolean ignoreScissorsInLava = false;
|
|
+ public double scissorsRunningDamage = 1D;
|
|
+ public float enderPearlDamage = 5.0F;
|
|
+ public int enderPearlCooldown = 20;
|
|
+ public int enderPearlCooldownCreative = 20;
|
|
+ public float enderPearlEndermiteChance = 0.05F;
|
|
+ public int glowBerriesEatGlowDuration = 0;
|
|
+ public boolean shulkerBoxItemDropContentsWhenDestroyed = true;
|
|
+ public boolean compassItemShowsBossBar = false;
|
|
+ public boolean snowballExtinguishesFire = false;
|
|
+ public boolean snowballExtinguishesCandles = false;
|
|
+ public boolean snowballExtinguishesCampfires = false;
|
|
+ private void itemSettings() {
|
|
+ itemImmuneToCactus.clear();
|
|
+ getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToCactus.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToCactus.add(item);
|
|
+ });
|
|
+ itemImmuneToExplosion.clear();
|
|
+ getList("gameplay-mechanics.item.immune.explosion", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToExplosion.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToExplosion.add(item);
|
|
+ });
|
|
+ itemImmuneToFire.clear();
|
|
+ getList("gameplay-mechanics.item.immune.fire", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToFire.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToFire.add(item);
|
|
+ });
|
|
+ itemImmuneToLightning.clear();
|
|
+ getList("gameplay-mechanics.item.immune.lightning", new ArrayList<>()).forEach(key -> {
|
|
+ if (key.toString().equals("*")) {
|
|
+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToLightning.add(item));
|
|
+ return;
|
|
+ }
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString()));
|
|
+ if (item != Items.AIR) itemImmuneToLightning.add(item);
|
|
+ });
|
|
+ dontRunWithScissors = getBoolean("gameplay-mechanics.item.shears.damage-if-sprinting", dontRunWithScissors);
|
|
+ ignoreScissorsInWater = getBoolean("gameplay-mechanics.item.shears.ignore-in-water", ignoreScissorsInWater);
|
|
+ ignoreScissorsInLava = getBoolean("gameplay-mechanics.item.shears.ignore-in-lava", ignoreScissorsInLava);
|
|
+ scissorsRunningDamage = getDouble("gameplay-mechanics.item.shears.sprinting-damage", scissorsRunningDamage);
|
|
+ enderPearlDamage = (float) getDouble("gameplay-mechanics.item.ender-pearl.damage", enderPearlDamage);
|
|
+ enderPearlCooldown = getInt("gameplay-mechanics.item.ender-pearl.cooldown", enderPearlCooldown);
|
|
+ enderPearlCooldownCreative = getInt("gameplay-mechanics.item.ender-pearl.creative-cooldown", enderPearlCooldownCreative);
|
|
+ enderPearlEndermiteChance = (float) getDouble("gameplay-mechanics.item.ender-pearl.endermite-spawn-chance", enderPearlEndermiteChance);
|
|
+ glowBerriesEatGlowDuration = getInt("gameplay-mechanics.item.glow_berries.eat-glow-duration", glowBerriesEatGlowDuration);
|
|
+ shulkerBoxItemDropContentsWhenDestroyed = getBoolean("gameplay-mechanics.item.shulker_box.drop-contents-when-destroyed", shulkerBoxItemDropContentsWhenDestroyed);
|
|
+ compassItemShowsBossBar = getBoolean("gameplay-mechanics.item.compass.holding-shows-bossbar", compassItemShowsBossBar);
|
|
+ snowballExtinguishesFire = getBoolean("gameplay-mechanics.item.snowball.extinguish.fire", snowballExtinguishesFire);
|
|
+ snowballExtinguishesCandles = getBoolean("gameplay-mechanics.item.snowball.extinguish.candles", snowballExtinguishesCandles);
|
|
+ snowballExtinguishesCampfires = getBoolean("gameplay-mechanics.item.snowball.extinguish.campfires", snowballExtinguishesCampfires);
|
|
+ }
|
|
+
|
|
+ public double minecartMaxSpeed = 0.4D;
|
|
+ public boolean minecartPlaceAnywhere = false;
|
|
+ public boolean minecartControllable = false;
|
|
+ public float minecartControllableStepHeight = 1.0F;
|
|
+ public double minecartControllableHopBoost = 0.5D;
|
|
+ public boolean minecartControllableFallDamage = true;
|
|
+ public double minecartControllableBaseSpeed = 0.1D;
|
|
+ public Map<Block, Double> minecartControllableBlockSpeeds = new HashMap<>();
|
|
+ public double poweredRailBoostModifier = 0.06;
|
|
+ private void minecartSettings() {
|
|
+ if (PurpurConfig.version < 12) {
|
|
+ boolean oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.place-anywhere", minecartPlaceAnywhere);
|
|
+ set("gameplay-mechanics.controllable-minecarts.place-anywhere", null);
|
|
+ set("gameplay-mechanics.minecart.place-anywhere", oldBool);
|
|
+ oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.enabled", minecartControllable);
|
|
+ set("gameplay-mechanics.controllable-minecarts.enabled", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.enabled", oldBool);
|
|
+ double oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.step-height", minecartControllableStepHeight);
|
|
+ set("gameplay-mechanics.controllable-minecarts.step-height", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.step-height", oldDouble);
|
|
+ oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.hop-boost", minecartControllableHopBoost);
|
|
+ set("gameplay-mechanics.controllable-minecarts.hop-boost", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.hop-boost", oldDouble);
|
|
+ oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.fall-damage", minecartControllableFallDamage);
|
|
+ set("gameplay-mechanics.controllable-minecarts.fall-damage", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.fall-damage", oldBool);
|
|
+ oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.base-speed", minecartControllableBaseSpeed);
|
|
+ set("gameplay-mechanics.controllable-minecarts.base-speed", null);
|
|
+ set("gameplay-mechanics.minecart.controllable.base-speed", oldDouble);
|
|
+ ConfigurationSection section = getConfigurationSection("gameplay-mechanics.controllable-minecarts.block-speed");
|
|
+ if (section != null) {
|
|
+ for (String key : section.getKeys(false)) {
|
|
+ if ("grass-block".equals(key)) key = "grass_block"; // oopsie
|
|
+ oldDouble = section.getDouble(key, minecartControllableBaseSpeed);
|
|
+ set("gameplay-mechanics.controllable-minecarts.block-speed." + key, null);
|
|
+ set("gameplay-mechanics.minecart.controllable.block-speed." + key, oldDouble);
|
|
+ }
|
|
+ set("gameplay-mechanics.controllable-minecarts.block-speed", null);
|
|
+ }
|
|
+ set("gameplay-mechanics.controllable-minecarts", null);
|
|
+ }
|
|
+
|
|
+ minecartMaxSpeed = getDouble("gameplay-mechanics.minecart.max-speed", minecartMaxSpeed);
|
|
+ minecartPlaceAnywhere = getBoolean("gameplay-mechanics.minecart.place-anywhere", minecartPlaceAnywhere);
|
|
+ minecartControllable = getBoolean("gameplay-mechanics.minecart.controllable.enabled", minecartControllable);
|
|
+ minecartControllableStepHeight = (float) getDouble("gameplay-mechanics.minecart.controllable.step-height", minecartControllableStepHeight);
|
|
+ minecartControllableHopBoost = getDouble("gameplay-mechanics.minecart.controllable.hop-boost", minecartControllableHopBoost);
|
|
+ minecartControllableFallDamage = getBoolean("gameplay-mechanics.minecart.controllable.fall-damage", minecartControllableFallDamage);
|
|
+ minecartControllableBaseSpeed = getDouble("gameplay-mechanics.minecart.controllable.base-speed", minecartControllableBaseSpeed);
|
|
+ ConfigurationSection section = getConfigurationSection("gameplay-mechanics.minecart.controllable.block-speed");
|
|
+ if (section != null) {
|
|
+ for (String key : section.getKeys(false)) {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key));
|
|
+ if (block != Blocks.AIR) {
|
|
+ minecartControllableBlockSpeeds.put(block, section.getDouble(key, minecartControllableBaseSpeed));
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ set("gameplay-mechanics.minecart.controllable.block-speed.grass_block", 0.3D);
|
|
+ set("gameplay-mechanics.minecart.controllable.block-speed.stone", 0.5D);
|
|
+ }
|
|
+ poweredRailBoostModifier = getDouble("gameplay-mechanics.minecart.powered-rail.boost-modifier", poweredRailBoostModifier);
|
|
+ }
|
|
+
|
|
+ public float entityHealthRegenAmount = 1.0F;
|
|
+ public float entityMinimalHealthPoison = 1.0F;
|
|
+ public float entityPoisonDegenerationAmount = 1.0F;
|
|
+ public float entityWitherDegenerationAmount = 1.0F;
|
|
+ public float humanHungerExhaustionAmount = 0.005F;
|
|
+ public float humanSaturationRegenAmount = 1.0F;
|
|
+ private void mobEffectSettings() {
|
|
+ entityHealthRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.health-regen-amount", entityHealthRegenAmount);
|
|
+ entityMinimalHealthPoison = (float) getDouble("gameplay-mechanics.mob-effects.minimal-health-poison-amount", entityMinimalHealthPoison);
|
|
+ entityPoisonDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.poison-degeneration-amount", entityPoisonDegenerationAmount);
|
|
+ entityWitherDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.wither-degeneration-amount", entityWitherDegenerationAmount);
|
|
+ humanHungerExhaustionAmount = (float) getDouble("gameplay-mechanics.mob-effects.hunger-exhaustion-amount", humanHungerExhaustionAmount);
|
|
+ humanSaturationRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.saturation-regen-amount", humanSaturationRegenAmount);
|
|
+ }
|
|
+
|
|
+ public boolean catSpawning;
|
|
+ public boolean patrolSpawning;
|
|
+ public boolean phantomSpawning;
|
|
+ public boolean villagerTraderSpawning;
|
|
+ public boolean villageSiegeSpawning;
|
|
+ public boolean mobSpawningIgnoreCreativePlayers = false;
|
|
+ private void mobSpawnerSettings() {
|
|
+ // values of "default" or null will default to true only if the world environment is normal (aka overworld)
|
|
+ Predicate<Boolean> predicate = (bool) -> (bool != null && bool) || (bool == null && environment == World.Environment.NORMAL);
|
|
+ catSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-cats", predicate);
|
|
+ patrolSpawning = getBoolean("gameplay-mechanics.mob-spawning.raid-patrols", predicate);
|
|
+ phantomSpawning = getBoolean("gameplay-mechanics.mob-spawning.phantoms", predicate);
|
|
+ villagerTraderSpawning = getBoolean("gameplay-mechanics.mob-spawning.wandering-traders", predicate);
|
|
+ villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate);
|
|
+ mobSpawningIgnoreCreativePlayers = getBoolean("gameplay-mechanics.mob-spawning.ignore-creative-players", mobSpawningIgnoreCreativePlayers);
|
|
+ }
|
|
+
|
|
+ public boolean disableObserverClocks = false;
|
|
+ private void observerSettings() {
|
|
+ disableObserverClocks = getBoolean("blocks.observer.disable-clock", disableObserverClocks);
|
|
+ }
|
|
+
|
|
+ public int playerNetheriteFireResistanceDuration = 0;
|
|
+ public int playerNetheriteFireResistanceAmplifier = 0;
|
|
+ public boolean playerNetheriteFireResistanceAmbient = false;
|
|
+ public boolean playerNetheriteFireResistanceShowParticles = false;
|
|
+ public boolean playerNetheriteFireResistanceShowIcon = true;
|
|
+ private void playerNetheriteFireResistance() {
|
|
+ playerNetheriteFireResistanceDuration = getInt("gameplay-mechanics.player.netherite-fire-resistance.duration", playerNetheriteFireResistanceDuration);
|
|
+ playerNetheriteFireResistanceAmplifier = getInt("gameplay-mechanics.player.netherite-fire-resistance.amplifier", playerNetheriteFireResistanceAmplifier);
|
|
+ playerNetheriteFireResistanceAmbient = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.ambient", playerNetheriteFireResistanceAmbient);
|
|
+ playerNetheriteFireResistanceShowParticles = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-particles", playerNetheriteFireResistanceShowParticles);
|
|
+ playerNetheriteFireResistanceShowIcon = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-icon", playerNetheriteFireResistanceShowIcon);
|
|
+ }
|
|
+
|
|
+ public boolean idleTimeoutKick = true;
|
|
+ public boolean idleTimeoutTickNearbyEntities = true;
|
|
+ public boolean idleTimeoutCountAsSleeping = false;
|
|
+ public boolean idleTimeoutUpdateTabList = false;
|
|
+ public boolean idleTimeoutTargetPlayer = true;
|
|
+ public int playerSpawnInvulnerableTicks = 60;
|
|
+ public boolean playerInvulnerableWhileAcceptingResourcePack = false;
|
|
+ public String playerDeathExpDropEquation = "expLevel * 7";
|
|
+ public int playerDeathExpDropMax = 100;
|
|
+ public boolean teleportIfOutsideBorder = false;
|
|
+ public boolean teleportOnNetherCeilingDamage = false;
|
|
+ public boolean totemOfUndyingWorksInInventory = false;
|
|
+ public boolean playerFixStuckPortal = false;
|
|
+ public boolean creativeOnePunch = false;
|
|
+ public boolean playerSleepNearMonsters = false;
|
|
+ public boolean playersSkipNight = true;
|
|
+ public double playerCriticalDamageMultiplier = 1.5D;
|
|
+ public int playerBurpDelay = 10;
|
|
+ public boolean playerBurpWhenFull = false;
|
|
+ public int playerPortalWaitTime = 80;
|
|
+ public int playerCreativePortalWaitTime = 1;
|
|
+ public boolean playerRidableInWater = false;
|
|
+ public boolean playerRemoveBindingWithWeakness = false;
|
|
+ public int shiftRightClickRepairsMendingPoints = 0;
|
|
+ public int playerExpPickupDelay = 2;
|
|
+ public boolean playerVoidTrading = false;
|
|
+ private void playerSettings() {
|
|
+ if (PurpurConfig.version < 19) {
|
|
+ boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer);
|
|
+ set("gameplay-mechanics.player.idle-timeout.mods-target", null);
|
|
+ set("gameplay-mechanics.player.idle-timeout.mobs-target", oldVal);
|
|
+ }
|
|
+ idleTimeoutKick = System.getenv("PURPUR_FORCE_IDLE_KICK") == null ? getBoolean("gameplay-mechanics.player.idle-timeout.kick-if-idle", idleTimeoutKick) : Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK"));
|
|
+ idleTimeoutTickNearbyEntities = getBoolean("gameplay-mechanics.player.idle-timeout.tick-nearby-entities", idleTimeoutTickNearbyEntities);
|
|
+ idleTimeoutCountAsSleeping = getBoolean("gameplay-mechanics.player.idle-timeout.count-as-sleeping", idleTimeoutCountAsSleeping);
|
|
+ idleTimeoutUpdateTabList = getBoolean("gameplay-mechanics.player.idle-timeout.update-tab-list", idleTimeoutUpdateTabList);
|
|
+ idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer);
|
|
+ playerSpawnInvulnerableTicks = getInt("gameplay-mechanics.player.spawn-invulnerable-ticks", playerSpawnInvulnerableTicks);
|
|
+ playerInvulnerableWhileAcceptingResourcePack = getBoolean("gameplay-mechanics.player.invulnerable-while-accepting-resource-pack", playerInvulnerableWhileAcceptingResourcePack);
|
|
+ playerDeathExpDropEquation = getString("gameplay-mechanics.player.exp-dropped-on-death.equation", playerDeathExpDropEquation);
|
|
+ playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax);
|
|
+ teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder);
|
|
+ teleportOnNetherCeilingDamage = getBoolean("gameplay-mechanics.player.teleport-on-nether-ceiling-damage", teleportOnNetherCeilingDamage);
|
|
+ totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory);
|
|
+ playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal);
|
|
+ creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch);
|
|
+ playerSleepNearMonsters = getBoolean("gameplay-mechanics.player.sleep-ignore-nearby-mobs", playerSleepNearMonsters);
|
|
+ playersSkipNight = getBoolean("gameplay-mechanics.player.can-skip-night", playersSkipNight);
|
|
+ playerCriticalDamageMultiplier = getDouble("gameplay-mechanics.player.critical-damage-multiplier", playerCriticalDamageMultiplier);
|
|
+ playerBurpDelay = getInt("gameplay-mechanics.player.burp-delay", playerBurpDelay);
|
|
+ playerBurpWhenFull = getBoolean("gameplay-mechanics.player.burp-when-full", playerBurpWhenFull);
|
|
+ playerPortalWaitTime = getInt("gameplay-mechanics.player.portal-wait-time", playerPortalWaitTime);
|
|
+ playerCreativePortalWaitTime = getInt("gameplay-mechanics.player.creative-portal-wait-time", playerCreativePortalWaitTime);
|
|
+ playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater);
|
|
+ playerRemoveBindingWithWeakness = getBoolean("gameplay-mechanics.player.curse-of-binding.remove-with-weakness", playerRemoveBindingWithWeakness);
|
|
+ shiftRightClickRepairsMendingPoints = getInt("gameplay-mechanics.player.shift-right-click-repairs-mending-points", shiftRightClickRepairsMendingPoints);
|
|
+ playerExpPickupDelay = getInt("gameplay-mechanics.player.exp-pickup-delay-ticks", playerExpPickupDelay);
|
|
+ playerVoidTrading = getBoolean("gameplay-mechanics.player.allow-void-trading", playerVoidTrading);
|
|
+ }
|
|
+
|
|
+ private static boolean projectileDespawnRateSettingsMigrated = false;
|
|
+ private void projectileDespawnRateSettings() {
|
|
+ if (PurpurConfig.version < 28 && !projectileDespawnRateSettingsMigrated) {
|
|
+ migrateProjectileDespawnRateSettings(EntityType.DRAGON_FIREBALL);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.EGG);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.ENDER_PEARL);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.EXPERIENCE_BOTTLE);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.FIREWORK_ROCKET);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.FISHING_BOBBER);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.FIREBALL);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.LLAMA_SPIT);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.POTION);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.SHULKER_BULLET);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.SMALL_FIREBALL);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.SNOWBALL);
|
|
+ migrateProjectileDespawnRateSettings(EntityType.WITHER_SKULL);
|
|
+ //PufferfishConfig.save();
|
|
+ set("gameplay-mechanics.projectile-despawn-rates", null);
|
|
+ // pufferfish's entity_timeout is a global config
|
|
+ // we only want to migrate values from the
|
|
+ // default world (first world loaded)
|
|
+ projectileDespawnRateSettingsMigrated = true;
|
|
+ }
|
|
+ }
|
|
+ private void migrateProjectileDespawnRateSettings(EntityType<?> type) {
|
|
+ //String pufferName = "entity_timeouts." + type.id.toUpperCase(Locale.ROOT);
|
|
+ //int value = getInt("gameplay-mechanics.projectile-despawn-rates." + type.id, -1);
|
|
+ //if (value != -1 && PufferfishConfig.getRawInt(pufferName, -1) == -1) {
|
|
+ // PufferfishConfig.setInt(pufferName, value);
|
|
+ // type.ttl = value;
|
|
+ //}
|
|
+ }
|
|
+
|
|
+ public double bowProjectileOffset = 1.0D;
|
|
+ public double crossbowProjectileOffset = 1.0D;
|
|
+ public double eggProjectileOffset = 1.0D;
|
|
+ public double enderPearlProjectileOffset = 1.0D;
|
|
+ public double throwablePotionProjectileOffset = 1.0D;
|
|
+ public double tridentProjectileOffset = 1.0D;
|
|
+ public double snowballProjectileOffset = 1.0D;
|
|
+ private void projectileOffsetSettings() {
|
|
+ bowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.bow", bowProjectileOffset);
|
|
+ crossbowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.crossbow", crossbowProjectileOffset);
|
|
+ eggProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.egg", eggProjectileOffset);
|
|
+ enderPearlProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.ender-pearl", enderPearlProjectileOffset);
|
|
+ throwablePotionProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.throwable-potion", throwablePotionProjectileOffset);
|
|
+ tridentProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.trident", tridentProjectileOffset);
|
|
+ snowballProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.snowball", snowballProjectileOffset);
|
|
+ }
|
|
+
|
|
+ public int snowballDamage = -1;
|
|
+ private void snowballSettings() {
|
|
+ snowballDamage = getInt("gameplay-mechanics.projectile-damage.snowball", snowballDamage);
|
|
+ }
|
|
+
|
|
+ public List<Block> shovelTurnsBlockToGrassPath = new ArrayList<>();
|
|
+ private void shovelSettings() {
|
|
+ getList("gameplay-mechanics.shovel-turns-block-to-grass-path", new ArrayList<String>(){{
|
|
+ add("minecraft:coarse_dirt");
|
|
+ add("minecraft:dirt");
|
|
+ add("minecraft:grass_block");
|
|
+ add("minecraft:mycelium");
|
|
+ add("minecraft:podzol");
|
|
+ add("minecraft:rooted_dirt");
|
|
+ }}).forEach(key -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key.toString()));
|
|
+ if (block != Blocks.AIR) shovelTurnsBlockToGrassPath.add(block);
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public boolean silkTouchEnabled = false;
|
|
+ public String silkTouchSpawnerName = "<reset><white>Monster Spawner";
|
|
+ public List<String> silkTouchSpawnerLore = new ArrayList<>();
|
|
+ public List<Item> silkTouchTools = new ArrayList<>();
|
|
+ public int minimumSilkTouchSpawnerRequire = 1;
|
|
+ private void silkTouchSettings() {
|
|
+ if (PurpurConfig.version < 21) {
|
|
+ String oldName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName);
|
|
+ set("gameplay-mechanics.silk-touch.spawner-name", "<reset>" + ChatColor.toMM(oldName.replace("{mob}", "<mob>")));
|
|
+ List<String> list = new ArrayList<>();
|
|
+ getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a <mob>"))
|
|
+ .forEach(line -> list.add("<reset>" + ChatColor.toMM(line.toString().replace("{mob}", "<mob>"))));
|
|
+ set("gameplay-mechanics.silk-touch.spawner-lore", list);
|
|
+ }
|
|
+ silkTouchEnabled = getBoolean("gameplay-mechanics.silk-touch.enabled", silkTouchEnabled);
|
|
+ silkTouchSpawnerName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName);
|
|
+ minimumSilkTouchSpawnerRequire = getInt("gameplay-mechanics.silk-touch.minimal-level", minimumSilkTouchSpawnerRequire);
|
|
+ silkTouchSpawnerLore.clear();
|
|
+ getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a <mob>"))
|
|
+ .forEach(line -> silkTouchSpawnerLore.add(line.toString()));
|
|
+ silkTouchTools.clear();
|
|
+ getList("gameplay-mechanics.silk-touch.tools", List.of(
|
|
+ "minecraft:iron_pickaxe",
|
|
+ "minecraft:golden_pickaxe",
|
|
+ "minecraft:diamond_pickaxe",
|
|
+ "minecraft:netherite_pickaxe"
|
|
+ )).forEach(key -> {
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString()));
|
|
+ if (item != Items.AIR) silkTouchTools.add(item);
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public Map<Block, Strippable> axeStrippables = new HashMap<>();
|
|
+ public Map<Block, Waxable> axeWaxables = new HashMap<>();
|
|
+ public Map<Block, Weatherable> axeWeatherables = new HashMap<>();
|
|
+ public Map<Block, Tillable> hoeTillables = new HashMap<>();
|
|
+ public boolean hoeReplantsCrops = false;
|
|
+ public boolean hoeReplantsNetherWarts = false;
|
|
+ private void toolSettings() {
|
|
+ axeStrippables.clear();
|
|
+ axeWaxables.clear();
|
|
+ axeWeatherables.clear();
|
|
+ hoeTillables.clear();
|
|
+ if (PurpurConfig.version < 18) {
|
|
+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + ".tools.hoe.tilling");
|
|
+ if (section != null) {
|
|
+ PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tillables", section);
|
|
+ PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tilling", null);
|
|
+ }
|
|
+ section = PurpurConfig.config.getConfigurationSection("world-settings.default.tools.hoe.tilling");
|
|
+ if (section != null) {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.hoe.tillables", section);
|
|
+ PurpurConfig.config.set("world-settings.default.tools.hoe.tilling", null);
|
|
+ }
|
|
+ }
|
|
+ if (PurpurConfig.version < 29) {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap<String, Double>()));
|
|
+ }
|
|
+ if (PurpurConfig.version < 32) {
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap<String, Double>()));
|
|
+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap<String, Double>()));
|
|
+ }
|
|
+ getMap("tools.axe.strippables", Map.ofEntries(
|
|
+ Map.entry("minecraft:oak_wood", Map.of("into", "minecraft:stripped_oak_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oak_log", Map.of("into", "minecraft:stripped_oak_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dark_oak_wood", Map.of("into", "minecraft:stripped_dark_oak_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dark_oak_log", Map.of("into", "minecraft:stripped_dark_oak_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:acacia_wood", Map.of("into", "minecraft:stripped_acacia_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:acacia_log", Map.of("into", "minecraft:stripped_acacia_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:birch_wood", Map.of("into", "minecraft:stripped_birch_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:birch_log", Map.of("into", "minecraft:stripped_birch_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:jungle_wood", Map.of("into", "minecraft:stripped_jungle_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:jungle_log", Map.of("into", "minecraft:stripped_jungle_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:spruce_wood", Map.of("into", "minecraft:stripped_spruce_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:spruce_log", Map.of("into", "minecraft:stripped_spruce_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:warped_stem", Map.of("into", "minecraft:stripped_warped_stem", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:warped_hyphae", Map.of("into", "minecraft:stripped_warped_hyphae", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:crimson_stem", Map.of("into", "minecraft:stripped_crimson_stem", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:crimson_hyphae", Map.of("into", "minecraft:stripped_crimson_hyphae", "drops", new HashMap<String, Double>())))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + "`"); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.strippables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ axeStrippables.put(block, new Strippable(into, drops));
|
|
+ });
|
|
+ getMap("tools.axe.waxables", Map.ofEntries(
|
|
+ Map.entry("minecraft:waxed_copper_block", Map.of("into", "minecraft:copper_block", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_copper", Map.of("into", "minecraft:oxidized_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_cut_copper", Map.of("into", "minecraft:oxidized_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_cut_copper_slab", Map.of("into", "minecraft:oxidized_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_exposed_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_weathered_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:waxed_oxidized_cut_copper_stairs", Map.of("into", "minecraft:oxidized_cut_copper_stairs", "drops", new HashMap<String, Double>())))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + "`"); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.waxables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ axeWaxables.put(block, new Waxable(into, drops));
|
|
+ });
|
|
+ getMap("tools.axe.weatherables", Map.ofEntries(
|
|
+ Map.entry("minecraft:exposed_copper", Map.of("into", "minecraft:copper_block", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:exposed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:weathered_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:oxidized_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap<String, Double>())))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + "`"); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.weatherables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ axeWeatherables.put(block, new Weatherable(into, drops));
|
|
+ });
|
|
+ getMap("tools.hoe.tillables", Map.ofEntries(
|
|
+ Map.entry("minecraft:grass_block", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dirt_path", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:dirt", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:coarse_dirt", Map.of("condition", "air_above", "into", "minecraft:dirt", "drops", new HashMap<String, Double>())),
|
|
+ Map.entry("minecraft:rooted_dirt", Map.of("condition", "always", "into", "minecraft:dirt", "drops", Map.of("minecraft:hanging_roots", 1.0D))))
|
|
+ ).forEach((blockId, obj) -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId));
|
|
+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables`: " + blockId); return; }
|
|
+ if (!(obj instanceof Map<?, ?> map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + "`"); return; }
|
|
+ String conditionId = (String) map.get("condition");
|
|
+ Tillable.Condition condition = Tillable.Condition.get(conditionId);
|
|
+ if (condition == null) { PurpurConfig.log(Level.SEVERE, "Invalid condition for `tools.hoe.tillables." + blockId + ".condition`: " + conditionId); return; }
|
|
+ String intoId = (String) map.get("into");
|
|
+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId));
|
|
+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables." + blockId + ".into`: " + intoId); return; }
|
|
+ Object dropsObj = map.get("drops");
|
|
+ if (!(dropsObj instanceof Map<?, ?> dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + ".drops`"); return; }
|
|
+ Map<Item, Double> drops = new HashMap<>();
|
|
+ dropsMap.forEach((itemId, chance) -> {
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString()));
|
|
+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.hoe.tillables." + blockId + ".drops`: " + itemId); return; }
|
|
+ drops.put(item, (double) chance);
|
|
+ });
|
|
+ hoeTillables.put(block, new Tillable(condition, into, drops));
|
|
+ });
|
|
+ hoeReplantsCrops = getBoolean("tools.hoe.replant-crops", hoeReplantsCrops);
|
|
+ hoeReplantsNetherWarts = getBoolean("tools.hoe.replant-nether-warts", hoeReplantsNetherWarts);
|
|
+ }
|
|
+
|
|
+ public boolean anvilAllowColors = false;
|
|
+ public boolean anvilColorsUseMiniMessage;
|
|
+ public int anvilRepairIngotsAmount = 0;
|
|
+ public int anvilDamageObsidianAmount = 0;
|
|
+ private void anvilSettings() {
|
|
+ anvilAllowColors = getBoolean("blocks.anvil.allow-colors", anvilAllowColors);
|
|
+ anvilColorsUseMiniMessage = getBoolean("blocks.anvil.use-mini-message", anvilColorsUseMiniMessage);
|
|
+ anvilRepairIngotsAmount = getInt("blocks.anvil.iron-ingots-used-for-repair", anvilRepairIngotsAmount);
|
|
+ anvilDamageObsidianAmount = getInt("blocks.anvil.obsidian-used-for-damage", anvilDamageObsidianAmount);
|
|
+ }
|
|
+
|
|
+ public double azaleaGrowthChance = 0.0D;
|
|
+ private void azaleaSettings() {
|
|
+ azaleaGrowthChance = getDouble("blocks.azalea.growth-chance", azaleaGrowthChance);
|
|
+ }
|
|
+
|
|
+ public int beaconLevelOne = 20;
|
|
+ public int beaconLevelTwo = 30;
|
|
+ public int beaconLevelThree = 40;
|
|
+ public int beaconLevelFour = 50;
|
|
+ public boolean beaconAllowEffectsWithTintedGlass = false;
|
|
+ private void beaconSettings() {
|
|
+ beaconLevelOne = getInt("blocks.beacon.effect-range.level-1", beaconLevelOne);
|
|
+ beaconLevelTwo = getInt("blocks.beacon.effect-range.level-2", beaconLevelTwo);
|
|
+ beaconLevelThree = getInt("blocks.beacon.effect-range.level-3", beaconLevelThree);
|
|
+ beaconLevelFour = getInt("blocks.beacon.effect-range.level-4", beaconLevelFour);
|
|
+ beaconAllowEffectsWithTintedGlass = getBoolean("blocks.beacon.allow-effects-with-tinted-glass", beaconAllowEffectsWithTintedGlass);
|
|
+ }
|
|
+
|
|
+ public boolean bedExplode = true;
|
|
+ public boolean bedExplodeOnVillagerSleep = false;
|
|
+ public double bedExplosionPower = 5.0D;
|
|
+ public boolean bedExplosionFire = true;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ private void bedSettings() {
|
|
+ if (PurpurConfig.version < 31) {
|
|
+ if ("DESTROY".equals(getString("blocks.bed.explosion-effect", bedExplosionEffect.name()))) {
|
|
+ set("blocks.bed.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ }
|
|
+ bedExplode = getBoolean("blocks.bed.explode", bedExplode);
|
|
+ bedExplodeOnVillagerSleep = getBoolean("blocks.bed.explode-on-villager-sleep", bedExplodeOnVillagerSleep);
|
|
+ bedExplosionPower = getDouble("blocks.bed.explosion-power", bedExplosionPower);
|
|
+ bedExplosionFire = getBoolean("blocks.bed.explosion-fire", bedExplosionFire);
|
|
+ try {
|
|
+ bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.bed.explosion-effect", bedExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.bed.explosion-effect`! Using default of `BLOCK`");
|
|
+ bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public Map<Tilt, Integer> bigDripleafTiltDelay = new HashMap<>();
|
|
+ private void bigDripleafSettings() {
|
|
+ bigDripleafTiltDelay.clear();
|
|
+ getMap("blocks.big_dripleaf.tilt-delay", Map.ofEntries(
|
|
+ Map.entry("UNSTABLE", 10),
|
|
+ Map.entry("PARTIAL", 10),
|
|
+ Map.entry("FULL", 100))
|
|
+ ).forEach((tilt, delay) -> {
|
|
+ try {
|
|
+ bigDripleafTiltDelay.put(Tilt.valueOf(tilt), (int) delay);
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ PurpurConfig.log(Level.SEVERE, "Invalid big_dripleaf tilt key: " + tilt);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public boolean buddingAmethystSilkTouch = false;
|
|
+ private void buddingAmethystSettings() {
|
|
+ buddingAmethystSilkTouch = getBoolean("blocks.budding_amethyst.silk-touch", buddingAmethystSilkTouch);
|
|
+ }
|
|
+
|
|
+ public boolean cactusBreaksFromSolidNeighbors = true;
|
|
+ public boolean cactusAffectedByBonemeal = false;
|
|
+ private void cactusSettings() {
|
|
+ cactusBreaksFromSolidNeighbors = getBoolean("blocks.cactus.breaks-from-solid-neighbors", cactusBreaksFromSolidNeighbors);
|
|
+ cactusAffectedByBonemeal = getBoolean("blocks.cactus.affected-by-bonemeal", cactusAffectedByBonemeal);
|
|
+ }
|
|
+
|
|
+ public boolean sugarCanAffectedByBonemeal = false;
|
|
+ private void sugarCaneSettings() {
|
|
+ sugarCanAffectedByBonemeal = getBoolean("blocks.sugar_cane.affected-by-bonemeal", sugarCanAffectedByBonemeal);
|
|
+ }
|
|
+
|
|
+ public boolean netherWartAffectedByBonemeal = false;
|
|
+ private void netherWartSettings() {
|
|
+ netherWartAffectedByBonemeal = getBoolean("blocks.nether_wart.affected-by-bonemeal", netherWartAffectedByBonemeal);
|
|
+ }
|
|
+
|
|
+ public boolean campFireLitWhenPlaced = true;
|
|
+ private void campFireSettings() {
|
|
+ campFireLitWhenPlaced = getBoolean("blocks.campfire.lit-when-placed", campFireLitWhenPlaced);
|
|
+ }
|
|
+
|
|
+ public boolean chestOpenWithBlockOnTop = false;
|
|
+ private void chestSettings() {
|
|
+ chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop);
|
|
+ }
|
|
+
|
|
+ public boolean composterBulkProcess = false;
|
|
+ private void composterSettings() {
|
|
+ composterBulkProcess = getBoolean("blocks.composter.sneak-to-bulk-process", composterBulkProcess);
|
|
+ }
|
|
+
|
|
+ public boolean coralDieOutsideWater = true;
|
|
+ private void coralSettings() {
|
|
+ coralDieOutsideWater = getBoolean("blocks.coral.die-outside-water", coralDieOutsideWater);
|
|
+ }
|
|
+
|
|
+ public boolean dispenserApplyCursedArmor = true;
|
|
+ public boolean dispenserPlaceAnvils = false;
|
|
+ private void dispenserSettings() {
|
|
+ dispenserApplyCursedArmor = getBoolean("blocks.dispenser.apply-cursed-to-armor-slots", dispenserApplyCursedArmor);
|
|
+ dispenserPlaceAnvils = getBoolean("blocks.dispenser.place-anvils", dispenserPlaceAnvils);
|
|
+ }
|
|
+
|
|
+ public List<Block> doorRequiresRedstone = new ArrayList<>();
|
|
+ private void doorSettings() {
|
|
+ getList("blocks.door.requires-redstone", new ArrayList<String>()).forEach(key -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key.toString()));
|
|
+ if (!block.defaultBlockState().isAir()) {
|
|
+ doorRequiresRedstone.add(block);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public boolean dragonEggTeleport = true;
|
|
+ private void dragonEggSettings() {
|
|
+ dragonEggTeleport = getBoolean("blocks.dragon_egg.teleport", dragonEggTeleport);
|
|
+ }
|
|
+
|
|
+ public boolean baselessEndCrystalExplode = true;
|
|
+ public double baselessEndCrystalExplosionPower = 6.0D;
|
|
+ public boolean baselessEndCrystalExplosionFire = false;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ public boolean basedEndCrystalExplode = true;
|
|
+ public double basedEndCrystalExplosionPower = 6.0D;
|
|
+ public boolean basedEndCrystalExplosionFire = false;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ public int endCrystalCramming = 0;
|
|
+ private void endCrystalSettings() {
|
|
+ if (PurpurConfig.version < 31) {
|
|
+ if ("DESTROY".equals(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()))) {
|
|
+ set("blocks.end-crystal.baseless.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ if ("DESTROY".equals(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name()))) {
|
|
+ set("blocks.end-crystal.base.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ }
|
|
+ baselessEndCrystalExplode = getBoolean("blocks.end-crystal.baseless.explode", baselessEndCrystalExplode);
|
|
+ baselessEndCrystalExplosionPower = getDouble("blocks.end-crystal.baseless.explosion-power", baselessEndCrystalExplosionPower);
|
|
+ baselessEndCrystalExplosionFire = getBoolean("blocks.end-crystal.baseless.explosion-fire", baselessEndCrystalExplosionFire);
|
|
+ try {
|
|
+ baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.end-crystal.baseless.explosion-effect`! Using default of `BLOCK`");
|
|
+ baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ basedEndCrystalExplode = getBoolean("blocks.end-crystal.base.explode", basedEndCrystalExplode);
|
|
+ basedEndCrystalExplosionPower = getDouble("blocks.end-crystal.base.explosion-power", basedEndCrystalExplosionPower);
|
|
+ basedEndCrystalExplosionFire = getBoolean("blocks.end-crystal.base.explosion-fire", basedEndCrystalExplosionFire);
|
|
+ try {
|
|
+ basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.end-crystal.base.explosion-effect`! Using default of `BLOCK`");
|
|
+ basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ endCrystalCramming = getInt("blocks.end-crystal.cramming-amount", endCrystalCramming);
|
|
+ }
|
|
+
|
|
+ public boolean farmlandBypassMobGriefing = false;
|
|
+ public boolean farmlandGetsMoistFromBelow = false;
|
|
+ public boolean farmlandAlpha = false;
|
|
+ public boolean farmlandTramplingDisabled = false;
|
|
+ public boolean farmlandTramplingOnlyPlayers = false;
|
|
+ public boolean farmlandTramplingFeatherFalling = false;
|
|
+ public double farmlandTrampleHeight = -1D;
|
|
+ private void farmlandSettings() {
|
|
+ farmlandBypassMobGriefing = getBoolean("blocks.farmland.bypass-mob-griefing", farmlandBypassMobGriefing);
|
|
+ farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow);
|
|
+ farmlandAlpha = getBoolean("blocks.farmland.use-alpha-farmland", farmlandAlpha);
|
|
+ farmlandTramplingDisabled = getBoolean("blocks.farmland.disable-trampling", farmlandTramplingDisabled);
|
|
+ farmlandTramplingOnlyPlayers = getBoolean("blocks.farmland.only-players-trample", farmlandTramplingOnlyPlayers);
|
|
+ farmlandTramplingFeatherFalling = getBoolean("blocks.farmland.feather-fall-distance-affects-trampling", farmlandTramplingFeatherFalling);
|
|
+ farmlandTrampleHeight = getDouble("blocks.farmland.trample-height", farmlandTrampleHeight);
|
|
+ }
|
|
+
|
|
+ public double floweringAzaleaGrowthChance = 0.0D;
|
|
+ private void floweringAzaleaSettings() {
|
|
+ floweringAzaleaGrowthChance = getDouble("blocks.flowering_azalea.growth-chance", floweringAzaleaGrowthChance);
|
|
+ }
|
|
+
|
|
+ public boolean furnaceUseLavaFromUnderneath = false;
|
|
+ private void furnaceSettings() {
|
|
+ if (PurpurConfig.version < 17) {
|
|
+ furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath);
|
|
+ boolean oldValue = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath);
|
|
+ set("blocks.furnace.infinite-fuel", null);
|
|
+ set("blocks.furnace.use-lava-from-underneath", oldValue);
|
|
+ }
|
|
+ furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.use-lava-from-underneath", furnaceUseLavaFromUnderneath);
|
|
+ }
|
|
+
|
|
+ public boolean endPortalSafeTeleporting = true;
|
|
+ private void endPortalSettings() {
|
|
+ endPortalSafeTeleporting = getBoolean("blocks.end_portal.safe-teleporting", endPortalSafeTeleporting);
|
|
+ }
|
|
+
|
|
+ public boolean mobsSpawnOnPackedIce = true;
|
|
+ public boolean mobsSpawnOnBlueIce = true;
|
|
+ public boolean snowOnBlueIce = true;
|
|
+ private void iceSettings() {
|
|
+ mobsSpawnOnPackedIce = getBoolean("blocks.packed_ice.allow-mob-spawns", mobsSpawnOnPackedIce);
|
|
+ mobsSpawnOnBlueIce = getBoolean("blocks.blue_ice.allow-mob-spawns", mobsSpawnOnBlueIce);
|
|
+ snowOnBlueIce = getBoolean("blocks.blue_ice.allow-snow-formation", snowOnBlueIce);
|
|
+ }
|
|
+
|
|
+ public int lavaInfiniteRequiredSources = 2;
|
|
+ public int lavaSpeedNether = 10;
|
|
+ public int lavaSpeedNotNether = 30;
|
|
+ private void lavaSettings() {
|
|
+ lavaInfiniteRequiredSources = getInt("blocks.lava.infinite-required-sources", lavaInfiniteRequiredSources);
|
|
+ lavaSpeedNether = getInt("blocks.lava.speed.nether", lavaSpeedNether);
|
|
+ lavaSpeedNotNether = getInt("blocks.lava.speed.not-nether", lavaSpeedNotNether);
|
|
+ }
|
|
+
|
|
+ public int pistonBlockPushLimit = 12;
|
|
+ private void pistonSettings() {
|
|
+ pistonBlockPushLimit = getInt("blocks.piston.block-push-limit", pistonBlockPushLimit);
|
|
+ }
|
|
+
|
|
+ public boolean magmaBlockDamageWhenSneaking = false;
|
|
+ public boolean magmaBlockDamageWithFrostWalker = false;
|
|
+ private void magmaBlockSettings() {
|
|
+ magmaBlockDamageWhenSneaking = getBoolean("blocks.magma-block.damage-when-sneaking", magmaBlockDamageWhenSneaking);
|
|
+ magmaBlockDamageWithFrostWalker = getBoolean("blocks.magma-block.damage-with-frost-walker", magmaBlockDamageWithFrostWalker);
|
|
+ }
|
|
+
|
|
+ public boolean powderSnowBypassMobGriefing = false;
|
|
+ private void powderSnowSettings() {
|
|
+ powderSnowBypassMobGriefing = getBoolean("blocks.powder_snow.bypass-mob-griefing", powderSnowBypassMobGriefing);
|
|
+ }
|
|
+
|
|
+ public int railActivationRange = 8;
|
|
+ private void railSettings() {
|
|
+ railActivationRange = getInt("blocks.powered-rail.activation-range", railActivationRange);
|
|
+ }
|
|
+
|
|
+ public boolean respawnAnchorExplode = true;
|
|
+ public double respawnAnchorExplosionPower = 5.0D;
|
|
+ public boolean respawnAnchorExplosionFire = true;
|
|
+ public net.minecraft.world.level.Level.ExplosionInteraction respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ private void respawnAnchorSettings() {
|
|
+ if (PurpurConfig.version < 31) {
|
|
+ if ("DESTROY".equals(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name()))) {
|
|
+ set("blocks.respawn_anchor.explosion-effect", "BLOCK");
|
|
+ }
|
|
+ }
|
|
+ respawnAnchorExplode = getBoolean("blocks.respawn_anchor.explode", respawnAnchorExplode);
|
|
+ respawnAnchorExplosionPower = getDouble("blocks.respawn_anchor.explosion-power", respawnAnchorExplosionPower);
|
|
+ respawnAnchorExplosionFire = getBoolean("blocks.respawn_anchor.explosion-fire", respawnAnchorExplosionFire);
|
|
+ try {
|
|
+ respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ log(Level.SEVERE, "Unknown value for `blocks.respawn_anchor.explosion-effect`! Using default of `BLOCK`");
|
|
+ respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean fixSandDuping = true;
|
|
+ private void sandSettings() {
|
|
+ fixSandDuping = getBoolean("blocks.sand.fix-duping", fixSandDuping);
|
|
+ }
|
|
+
|
|
+ public boolean sculkShriekerCanSummonDefault = false;
|
|
+ private void sculkShriekerSettings() {
|
|
+ sculkShriekerCanSummonDefault = getBoolean("blocks.sculk_shrieker.can-summon-default", sculkShriekerCanSummonDefault);
|
|
+ }
|
|
+
|
|
+ public boolean shulkerBoxAllowOversizedStacks = false;
|
|
+ private void shulkerBoxSettings() {
|
|
+ shulkerBoxAllowOversizedStacks = getBoolean("blocks.shulker_box.allow-oversized-stacks", shulkerBoxAllowOversizedStacks);
|
|
+ }
|
|
+
|
|
+ public boolean signRightClickEdit = false;
|
|
+ public boolean signAllowColors = false;
|
|
+ private void signSettings() {
|
|
+ signRightClickEdit = getBoolean("blocks.sign.right-click-edit", signRightClickEdit);
|
|
+ signAllowColors = getBoolean("blocks.sign.allow-colors", signAllowColors);
|
|
+ }
|
|
+
|
|
+ public boolean slabHalfBreak = false;
|
|
+ private void slabSettings() {
|
|
+ slabHalfBreak = getBoolean("blocks.slab.break-individual-slabs-when-sneaking", slabHalfBreak);
|
|
+ }
|
|
+
|
|
+ public boolean spawnerDeactivateByRedstone = false;
|
|
+ public boolean spawnerFixMC238526 = false;
|
|
+ private void spawnerSettings() {
|
|
+ spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone);
|
|
+ spawnerFixMC238526 = getBoolean("blocks.spawner.fix-mc-238526", spawnerFixMC238526);
|
|
+ }
|
|
+
|
|
+ public int spongeAbsorptionArea = 64;
|
|
+ public int spongeAbsorptionRadius = 6;
|
|
+ public boolean spongeAbsorbsLava = false;
|
|
+ private void spongeSettings() {
|
|
+ spongeAbsorptionArea = getInt("blocks.sponge.absorption.area", spongeAbsorptionArea);
|
|
+ spongeAbsorptionRadius = getInt("blocks.sponge.absorption.radius", spongeAbsorptionRadius);
|
|
+ spongeAbsorbsLava = getBoolean("blocks.sponge.absorbs-lava", spongeAbsorbsLava);
|
|
+ }
|
|
+
|
|
+ public float stonecutterDamage = 0.0F;
|
|
+ private void stonecutterSettings() {
|
|
+ stonecutterDamage = (float) getDouble("blocks.stonecutter.damage", stonecutterDamage);
|
|
+ }
|
|
+
|
|
+ public boolean turtleEggsBreakFromExpOrbs = true;
|
|
+ public boolean turtleEggsBreakFromItems = true;
|
|
+ public boolean turtleEggsBreakFromMinecarts = true;
|
|
+ public boolean turtleEggsBypassMobGriefing = false;
|
|
+ public int turtleEggsRandomTickCrackChance = 500;
|
|
+ public boolean turtleEggsTramplingFeatherFalling = false;
|
|
+ private void turtleEggSettings() {
|
|
+ turtleEggsBreakFromExpOrbs = getBoolean("blocks.turtle_egg.break-from-exp-orbs", turtleEggsBreakFromExpOrbs);
|
|
+ turtleEggsBreakFromItems = getBoolean("blocks.turtle_egg.break-from-items", turtleEggsBreakFromItems);
|
|
+ turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts);
|
|
+ turtleEggsBypassMobGriefing = getBoolean("blocks.turtle_egg.bypass-mob-griefing", turtleEggsBypassMobGriefing);
|
|
+ turtleEggsRandomTickCrackChance = getInt("blocks.turtle_egg.random-tick-crack-chance", turtleEggsRandomTickCrackChance);
|
|
+ turtleEggsTramplingFeatherFalling = getBoolean("blocks.turtle_egg.feather-fall-distance-affects-trampling", turtleEggsTramplingFeatherFalling);
|
|
+ }
|
|
+
|
|
+ public int waterInfiniteRequiredSources = 2;
|
|
+ private void waterSources() {
|
|
+ waterInfiniteRequiredSources = getInt("blocks.water.infinite-required-sources", waterInfiniteRequiredSources);
|
|
+ }
|
|
+
|
|
+ public boolean babiesAreRidable = true;
|
|
+ public boolean untamedTamablesAreRidable = true;
|
|
+ public boolean useNightVisionWhenRiding = false;
|
|
+ public boolean useDismountsUnderwaterTag = true;
|
|
+ private void ridableSettings() {
|
|
+ babiesAreRidable = getBoolean("ridable-settings.babies-are-ridable", babiesAreRidable);
|
|
+ untamedTamablesAreRidable = getBoolean("ridable-settings.untamed-tamables-are-ridable", untamedTamablesAreRidable);
|
|
+ useNightVisionWhenRiding = getBoolean("ridable-settings.use-night-vision", useNightVisionWhenRiding);
|
|
+ useDismountsUnderwaterTag = getBoolean("ridable-settings.use-dismounts-underwater-tag", useDismountsUnderwaterTag);
|
|
+ }
|
|
+
|
|
+ public boolean allayRidable = false;
|
|
+ public boolean allayRidableInWater = true;
|
|
+ public boolean allayControllable = true;
|
|
+ public List<String> allayRespectNBT = new ArrayList<>();
|
|
+ private void allaySettings() {
|
|
+ allayRidable = getBoolean("mobs.allay.ridable", allayRidable);
|
|
+ allayRidableInWater = getBoolean("mobs.allay.ridable-in-water", allayRidableInWater);
|
|
+ allayControllable = getBoolean("mobs.allay.controllable", allayControllable);
|
|
+ allayRespectNBT.clear();
|
|
+ getList("mobs.allay.respect-nbt", new ArrayList<>()).forEach(key -> allayRespectNBT.add(key.toString()));
|
|
+ }
|
|
+
|
|
+ public boolean axolotlRidable = false;
|
|
+ public boolean axolotlControllable = true;
|
|
+ public double axolotlMaxHealth = 14.0D;
|
|
+ public int axolotlBreedingTicks = 6000;
|
|
+ public boolean axolotlTakeDamageFromWater = false;
|
|
+ public boolean axolotlAlwaysDropExp = false;
|
|
+ private void axolotlSettings() {
|
|
+ axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable);
|
|
+ axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable);
|
|
+ axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth);
|
|
+ axolotlBreedingTicks = getInt("mobs.axolotl.breeding-delay-ticks", axolotlBreedingTicks);
|
|
+ axolotlTakeDamageFromWater = getBoolean("mobs.axolotl.takes-damage-from-water", axolotlTakeDamageFromWater);
|
|
+ axolotlAlwaysDropExp = getBoolean("mobs.axolotl.always-drop-exp", axolotlAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean batRidable = false;
|
|
+ public boolean batRidableInWater = true;
|
|
+ public boolean batControllable = true;
|
|
+ public double batMaxY = 320D;
|
|
+ public double batMaxHealth = 6.0D;
|
|
+ public double batFollowRange = 16.0D;
|
|
+ public double batKnockbackResistance = 0.0D;
|
|
+ public double batMovementSpeed = 0.6D;
|
|
+ public double batFlyingSpeed = 0.6D;
|
|
+ public double batArmor = 0.0D;
|
|
+ public double batArmorToughness = 0.0D;
|
|
+ public double batAttackKnockback = 0.0D;
|
|
+ public boolean batTakeDamageFromWater = false;
|
|
+ public boolean batAlwaysDropExp = false;
|
|
+ private void batSettings() {
|
|
+ batRidable = getBoolean("mobs.bat.ridable", batRidable);
|
|
+ batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater);
|
|
+ batControllable = getBoolean("mobs.bat.controllable", batControllable);
|
|
+ batMaxY = getDouble("mobs.bat.ridable-max-y", batMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.bat.attributes.max-health", batMaxHealth);
|
|
+ set("mobs.bat.attributes.max-health", null);
|
|
+ set("mobs.bat.attributes.max_health", oldValue);
|
|
+ }
|
|
+ batMaxHealth = getDouble("mobs.bat.attributes.max_health", batMaxHealth);
|
|
+ batTakeDamageFromWater = getBoolean("mobs.bat.takes-damage-from-water", batTakeDamageFromWater);
|
|
+ batAlwaysDropExp = getBoolean("mobs.bat.always-drop-exp", batAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean beeRidable = false;
|
|
+ public boolean beeRidableInWater = true;
|
|
+ public boolean beeControllable = true;
|
|
+ public double beeMaxY = 320D;
|
|
+ public double beeMaxHealth = 10.0D;
|
|
+ public int beeBreedingTicks = 6000;
|
|
+ public boolean beeTakeDamageFromWater = false;
|
|
+ public boolean beeCanWorkAtNight = false;
|
|
+ public boolean beeCanWorkInRain = false;
|
|
+ public boolean beeAlwaysDropExp = false;
|
|
+ public boolean beeDiesAfterSting = true;
|
|
+ private void beeSettings() {
|
|
+ beeRidable = getBoolean("mobs.bee.ridable", beeRidable);
|
|
+ beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater);
|
|
+ beeControllable = getBoolean("mobs.bee.controllable", beeControllable);
|
|
+ beeMaxY = getDouble("mobs.bee.ridable-max-y", beeMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.bee.attributes.max-health", beeMaxHealth);
|
|
+ set("mobs.bee.attributes.max-health", null);
|
|
+ set("mobs.bee.attributes.max_health", oldValue);
|
|
+ }
|
|
+ beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth);
|
|
+ beeBreedingTicks = getInt("mobs.bee.breeding-delay-ticks", beeBreedingTicks);
|
|
+ beeTakeDamageFromWater = getBoolean("mobs.bee.takes-damage-from-water", beeTakeDamageFromWater);
|
|
+ beeCanWorkAtNight = getBoolean("mobs.bee.can-work-at-night", beeCanWorkAtNight);
|
|
+ beeCanWorkInRain = getBoolean("mobs.bee.can-work-in-rain", beeCanWorkInRain);
|
|
+ beeAlwaysDropExp = getBoolean("mobs.bee.always-drop-exp", beeAlwaysDropExp);
|
|
+ beeDiesAfterSting = getBoolean("mobs.bee.dies-after-sting", beeDiesAfterSting);
|
|
+ }
|
|
+
|
|
+ public boolean blazeRidable = false;
|
|
+ public boolean blazeRidableInWater = true;
|
|
+ public boolean blazeControllable = true;
|
|
+ public double blazeMaxY = 320D;
|
|
+ public double blazeMaxHealth = 20.0D;
|
|
+ public boolean blazeTakeDamageFromWater = true;
|
|
+ public boolean blazeAlwaysDropExp = false;
|
|
+ private void blazeSettings() {
|
|
+ blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable);
|
|
+ blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater);
|
|
+ blazeControllable = getBoolean("mobs.blaze.controllable", blazeControllable);
|
|
+ blazeMaxY = getDouble("mobs.blaze.ridable-max-y", blazeMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.blaze.attributes.max-health", blazeMaxHealth);
|
|
+ set("mobs.blaze.attributes.max-health", null);
|
|
+ set("mobs.blaze.attributes.max_health", oldValue);
|
|
+ }
|
|
+ blazeMaxHealth = getDouble("mobs.blaze.attributes.max_health", blazeMaxHealth);
|
|
+ blazeTakeDamageFromWater = getBoolean("mobs.blaze.takes-damage-from-water", blazeTakeDamageFromWater);
|
|
+ blazeAlwaysDropExp = getBoolean("mobs.blaze.always-drop-exp", blazeAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public int camelBreedingTicks = 6000;
|
|
+ public double camelMaxHealthMin = 32.0D;
|
|
+ public double camelMaxHealthMax = 32.0D;
|
|
+ public double camelJumpStrengthMin = 0.42D;
|
|
+ public double camelJumpStrengthMax = 0.42D;
|
|
+ public double camelMovementSpeedMin = 0.09D;
|
|
+ public double camelMovementSpeedMax = 0.09D;
|
|
+ private void camelSettings() {
|
|
+ camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin);
|
|
+ camelMaxHealthMax = getDouble("mobs.camel.attributes.max_health.max", camelMaxHealthMax);
|
|
+ camelJumpStrengthMin = getDouble("mobs.camel.attributes.jump_strength.min", camelJumpStrengthMin);
|
|
+ camelJumpStrengthMax = getDouble("mobs.camel.attributes.jump_strength.max", camelJumpStrengthMax);
|
|
+ camelMovementSpeedMin = getDouble("mobs.camel.attributes.movement_speed.min", camelMovementSpeedMin);
|
|
+ camelMovementSpeedMax = getDouble("mobs.camel.attributes.movement_speed.max", camelMovementSpeedMax);
|
|
+ camelBreedingTicks = getInt("mobs.camel.breeding-delay-ticks", camelBreedingTicks);
|
|
+ }
|
|
+
|
|
+ public boolean catRidable = false;
|
|
+ public boolean catRidableInWater = true;
|
|
+ public boolean catControllable = true;
|
|
+ public double catMaxHealth = 10.0D;
|
|
+ public int catSpawnDelay = 1200;
|
|
+ public int catSpawnSwampHutScanRange = 16;
|
|
+ public int catSpawnVillageScanRange = 48;
|
|
+ public int catBreedingTicks = 6000;
|
|
+ public DyeColor catDefaultCollarColor = DyeColor.RED;
|
|
+ public boolean catTakeDamageFromWater = false;
|
|
+ public boolean catAlwaysDropExp = false;
|
|
+ private void catSettings() {
|
|
+ catRidable = getBoolean("mobs.cat.ridable", catRidable);
|
|
+ catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater);
|
|
+ catControllable = getBoolean("mobs.cat.controllable", catControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cat.attributes.max-health", catMaxHealth);
|
|
+ set("mobs.cat.attributes.max-health", null);
|
|
+ set("mobs.cat.attributes.max_health", oldValue);
|
|
+ }
|
|
+ catMaxHealth = getDouble("mobs.cat.attributes.max_health", catMaxHealth);
|
|
+ catSpawnDelay = getInt("mobs.cat.spawn-delay", catSpawnDelay);
|
|
+ catSpawnSwampHutScanRange = getInt("mobs.cat.scan-range-for-other-cats.swamp-hut", catSpawnSwampHutScanRange);
|
|
+ catSpawnVillageScanRange = getInt("mobs.cat.scan-range-for-other-cats.village", catSpawnVillageScanRange);
|
|
+ catBreedingTicks = getInt("mobs.cat.breeding-delay-ticks", catBreedingTicks);
|
|
+ try {
|
|
+ catDefaultCollarColor = DyeColor.valueOf(getString("mobs.cat.default-collar-color", wolfDefaultCollarColor.name()));
|
|
+ } catch (IllegalArgumentException ignore) {
|
|
+ catDefaultCollarColor = DyeColor.RED;
|
|
+ }
|
|
+ catTakeDamageFromWater = getBoolean("mobs.cat.takes-damage-from-water", catTakeDamageFromWater);
|
|
+ catAlwaysDropExp = getBoolean("mobs.cat.always-drop-exp", catAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean caveSpiderRidable = false;
|
|
+ public boolean caveSpiderRidableInWater = true;
|
|
+ public boolean caveSpiderControllable = true;
|
|
+ public double caveSpiderMaxHealth = 12.0D;
|
|
+ public boolean caveSpiderTakeDamageFromWater = false;
|
|
+ public boolean caveSpiderAlwaysDropExp = false;
|
|
+ private void caveSpiderSettings() {
|
|
+ caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable);
|
|
+ caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater);
|
|
+ caveSpiderControllable = getBoolean("mobs.cave_spider.controllable", caveSpiderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cave_spider.attributes.max-health", caveSpiderMaxHealth);
|
|
+ set("mobs.cave_spider.attributes.max-health", null);
|
|
+ set("mobs.cave_spider.attributes.max_health", oldValue);
|
|
+ }
|
|
+ caveSpiderMaxHealth = getDouble("mobs.cave_spider.attributes.max_health", caveSpiderMaxHealth);
|
|
+ caveSpiderTakeDamageFromWater = getBoolean("mobs.cave_spider.takes-damage-from-water", caveSpiderTakeDamageFromWater);
|
|
+ caveSpiderAlwaysDropExp = getBoolean("mobs.cave_spider.always-drop-exp", caveSpiderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean chickenRidable = false;
|
|
+ public boolean chickenRidableInWater = false;
|
|
+ public boolean chickenControllable = true;
|
|
+ public double chickenMaxHealth = 4.0D;
|
|
+ public boolean chickenRetaliate = false;
|
|
+ public int chickenBreedingTicks = 6000;
|
|
+ public boolean chickenTakeDamageFromWater = false;
|
|
+ public boolean chickenAlwaysDropExp = false;
|
|
+ private void chickenSettings() {
|
|
+ chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable);
|
|
+ chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater);
|
|
+ chickenControllable = getBoolean("mobs.chicken.controllable", chickenControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.chicken.attributes.max-health", chickenMaxHealth);
|
|
+ set("mobs.chicken.attributes.max-health", null);
|
|
+ set("mobs.chicken.attributes.max_health", oldValue);
|
|
+ }
|
|
+ chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth);
|
|
+ chickenRetaliate = getBoolean("mobs.chicken.retaliate", chickenRetaliate);
|
|
+ chickenBreedingTicks = getInt("mobs.chicken.breeding-delay-ticks", chickenBreedingTicks);
|
|
+ chickenTakeDamageFromWater = getBoolean("mobs.chicken.takes-damage-from-water", chickenTakeDamageFromWater);
|
|
+ chickenAlwaysDropExp = getBoolean("mobs.chicken.always-drop-exp", chickenAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean codRidable = false;
|
|
+ public boolean codControllable = true;
|
|
+ public double codMaxHealth = 3.0D;
|
|
+ public boolean codTakeDamageFromWater = false;
|
|
+ public boolean codAlwaysDropExp = false;
|
|
+ private void codSettings() {
|
|
+ codRidable = getBoolean("mobs.cod.ridable", codRidable);
|
|
+ codControllable = getBoolean("mobs.cod.controllable", codControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cod.attributes.max-health", codMaxHealth);
|
|
+ set("mobs.cod.attributes.max-health", null);
|
|
+ set("mobs.cod.attributes.max_health", oldValue);
|
|
+ }
|
|
+ codMaxHealth = getDouble("mobs.cod.attributes.max_health", codMaxHealth);
|
|
+ codTakeDamageFromWater = getBoolean("mobs.cod.takes-damage-from-water", codTakeDamageFromWater);
|
|
+ codAlwaysDropExp = getBoolean("mobs.cod.always-drop-exp", codAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean cowRidable = false;
|
|
+ public boolean cowRidableInWater = true;
|
|
+ public boolean cowControllable = true;
|
|
+ public double cowMaxHealth = 10.0D;
|
|
+ public int cowFeedMushrooms = 0;
|
|
+ public int cowBreedingTicks = 6000;
|
|
+ public boolean cowTakeDamageFromWater = false;
|
|
+ public double cowNaturallyAggressiveToPlayersChance = 0.0D;
|
|
+ public double cowNaturallyAggressiveToPlayersDamage = 2.0D;
|
|
+ public boolean cowAlwaysDropExp = false;
|
|
+ private void cowSettings() {
|
|
+ if (PurpurConfig.version < 22) {
|
|
+ double oldValue = getDouble("mobs.cow.naturally-aggressive-to-players-chance", cowNaturallyAggressiveToPlayersChance);
|
|
+ set("mobs.cow.naturally-aggressive-to-players-chance", null);
|
|
+ set("mobs.cow.naturally-aggressive-to-players.chance", oldValue);
|
|
+ }
|
|
+ cowRidable = getBoolean("mobs.cow.ridable", cowRidable);
|
|
+ cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater);
|
|
+ cowControllable = getBoolean("mobs.cow.controllable", cowControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.cow.attributes.max-health", cowMaxHealth);
|
|
+ set("mobs.cow.attributes.max-health", null);
|
|
+ set("mobs.cow.attributes.max_health", oldValue);
|
|
+ }
|
|
+ cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth);
|
|
+ cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms);
|
|
+ cowBreedingTicks = getInt("mobs.cow.breeding-delay-ticks", cowBreedingTicks);
|
|
+ cowTakeDamageFromWater = getBoolean("mobs.cow.takes-damage-from-water", cowTakeDamageFromWater);
|
|
+ cowNaturallyAggressiveToPlayersChance = getDouble("mobs.cow.naturally-aggressive-to-players.chance", cowNaturallyAggressiveToPlayersChance);
|
|
+ cowNaturallyAggressiveToPlayersDamage = getDouble("mobs.cow.naturally-aggressive-to-players.damage", cowNaturallyAggressiveToPlayersDamage);
|
|
+ cowAlwaysDropExp = getBoolean("mobs.cow.always-drop-exp", cowAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean creeperRidable = false;
|
|
+ public boolean creeperRidableInWater = true;
|
|
+ public boolean creeperControllable = true;
|
|
+ public double creeperMaxHealth = 20.0D;
|
|
+ public double creeperChargedChance = 0.0D;
|
|
+ public boolean creeperAllowGriefing = true;
|
|
+ public boolean creeperBypassMobGriefing = false;
|
|
+ public boolean creeperTakeDamageFromWater = false;
|
|
+ public boolean creeperExplodeWhenKilled = false;
|
|
+ public boolean creeperHealthRadius = false;
|
|
+ public boolean creeperAlwaysDropExp = false;
|
|
+ public double creeperHeadVisibilityPercent = 0.5D;
|
|
+ public boolean creeperEncircleTarget = false;
|
|
+ private void creeperSettings() {
|
|
+ creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable);
|
|
+ creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater);
|
|
+ creeperControllable = getBoolean("mobs.creeper.controllable", creeperControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.creeper.attributes.max-health", creeperMaxHealth);
|
|
+ set("mobs.creeper.attributes.max-health", null);
|
|
+ set("mobs.creeper.attributes.max_health", oldValue);
|
|
+ }
|
|
+ creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth);
|
|
+ creeperChargedChance = getDouble("mobs.creeper.naturally-charged-chance", creeperChargedChance);
|
|
+ creeperAllowGriefing = getBoolean("mobs.creeper.allow-griefing", creeperAllowGriefing);
|
|
+ creeperBypassMobGriefing = getBoolean("mobs.creeper.bypass-mob-griefing", creeperBypassMobGriefing);
|
|
+ creeperTakeDamageFromWater = getBoolean("mobs.creeper.takes-damage-from-water", creeperTakeDamageFromWater);
|
|
+ creeperExplodeWhenKilled = getBoolean("mobs.creeper.explode-when-killed", creeperExplodeWhenKilled);
|
|
+ creeperHealthRadius = getBoolean("mobs.creeper.health-impacts-explosion", creeperHealthRadius);
|
|
+ creeperAlwaysDropExp = getBoolean("mobs.creeper.always-drop-exp", creeperAlwaysDropExp);
|
|
+ creeperHeadVisibilityPercent = getDouble("mobs.creeper.head-visibility-percent", creeperHeadVisibilityPercent);
|
|
+ creeperEncircleTarget = getBoolean("mobs.creeper.encircle-target", creeperEncircleTarget);
|
|
+ }
|
|
+
|
|
+ public boolean dolphinRidable = false;
|
|
+ public boolean dolphinControllable = true;
|
|
+ public int dolphinSpitCooldown = 20;
|
|
+ public float dolphinSpitSpeed = 1.0F;
|
|
+ public float dolphinSpitDamage = 2.0F;
|
|
+ public double dolphinMaxHealth = 10.0D;
|
|
+ public boolean dolphinDisableTreasureSearching = false;
|
|
+ public boolean dolphinTakeDamageFromWater = false;
|
|
+ public double dolphinNaturallyAggressiveToPlayersChance = 0.0D;
|
|
+ public boolean dolphinAlwaysDropExp = false;
|
|
+ private void dolphinSettings() {
|
|
+ dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable);
|
|
+ dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable);
|
|
+ dolphinSpitCooldown = getInt("mobs.dolphin.spit.cooldown", dolphinSpitCooldown);
|
|
+ dolphinSpitSpeed = (float) getDouble("mobs.dolphin.spit.speed", dolphinSpitSpeed);
|
|
+ dolphinSpitDamage = (float) getDouble("mobs.dolphin.spit.damage", dolphinSpitDamage);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.dolphin.attributes.max-health", dolphinMaxHealth);
|
|
+ set("mobs.dolphin.attributes.max-health", null);
|
|
+ set("mobs.dolphin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth);
|
|
+ dolphinDisableTreasureSearching = getBoolean("mobs.dolphin.disable-treasure-searching", dolphinDisableTreasureSearching);
|
|
+ dolphinTakeDamageFromWater = getBoolean("mobs.dolphin.takes-damage-from-water", dolphinTakeDamageFromWater);
|
|
+ dolphinNaturallyAggressiveToPlayersChance = getDouble("mobs.dolphin.naturally-aggressive-to-players-chance", dolphinNaturallyAggressiveToPlayersChance);
|
|
+ dolphinAlwaysDropExp = getBoolean("mobs.dolphin.always-drop-exp", dolphinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean donkeyRidableInWater = false;
|
|
+ public double donkeyMaxHealthMin = 15.0D;
|
|
+ public double donkeyMaxHealthMax = 30.0D;
|
|
+ public double donkeyJumpStrengthMin = 0.5D;
|
|
+ public double donkeyJumpStrengthMax = 0.5D;
|
|
+ public double donkeyMovementSpeedMin = 0.175D;
|
|
+ public double donkeyMovementSpeedMax = 0.175D;
|
|
+ public int donkeyBreedingTicks = 6000;
|
|
+ public boolean donkeyTakeDamageFromWater = false;
|
|
+ public boolean donkeyAlwaysDropExp = false;
|
|
+ private void donkeySettings() {
|
|
+ donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.donkey.attributes.max-health.min", donkeyMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.donkey.attributes.max-health.max", donkeyMaxHealthMax);
|
|
+ set("mobs.donkey.attributes.max-health", null);
|
|
+ set("mobs.donkey.attributes.max_health.min", oldMin);
|
|
+ set("mobs.donkey.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ donkeyMaxHealthMin = getDouble("mobs.donkey.attributes.max_health.min", donkeyMaxHealthMin);
|
|
+ donkeyMaxHealthMax = getDouble("mobs.donkey.attributes.max_health.max", donkeyMaxHealthMax);
|
|
+ donkeyJumpStrengthMin = getDouble("mobs.donkey.attributes.jump_strength.min", donkeyJumpStrengthMin);
|
|
+ donkeyJumpStrengthMax = getDouble("mobs.donkey.attributes.jump_strength.max", donkeyJumpStrengthMax);
|
|
+ donkeyMovementSpeedMin = getDouble("mobs.donkey.attributes.movement_speed.min", donkeyMovementSpeedMin);
|
|
+ donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax);
|
|
+ donkeyBreedingTicks = getInt("mobs.donkey.breeding-delay-ticks", donkeyBreedingTicks);
|
|
+ donkeyTakeDamageFromWater = getBoolean("mobs.donkey.takes-damage-from-water", donkeyTakeDamageFromWater);
|
|
+ donkeyAlwaysDropExp = getBoolean("mobs.donkey.always-drop-exp", donkeyAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean drownedRidable = false;
|
|
+ public boolean drownedRidableInWater = true;
|
|
+ public boolean drownedControllable = true;
|
|
+ public double drownedMaxHealth = 20.0D;
|
|
+ public double drownedSpawnReinforcements = 0.1D;
|
|
+ public boolean drownedJockeyOnlyBaby = true;
|
|
+ public double drownedJockeyChance = 0.05D;
|
|
+ public boolean drownedJockeyTryExistingChickens = true;
|
|
+ public boolean drownedTakeDamageFromWater = false;
|
|
+ public boolean drownedBreakDoors = false;
|
|
+ public boolean drownedAlwaysDropExp = false;
|
|
+ private void drownedSettings() {
|
|
+ drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable);
|
|
+ drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater);
|
|
+ drownedControllable = getBoolean("mobs.drowned.controllable", drownedControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.drowned.attributes.max-health", drownedMaxHealth);
|
|
+ set("mobs.drowned.attributes.max-health", null);
|
|
+ set("mobs.drowned.attributes.max_health", oldValue);
|
|
+ }
|
|
+ drownedMaxHealth = getDouble("mobs.drowned.attributes.max_health", drownedMaxHealth);
|
|
+ drownedSpawnReinforcements = getDouble("mobs.drowned.attributes.spawn_reinforcements", drownedSpawnReinforcements);
|
|
+ drownedJockeyOnlyBaby = getBoolean("mobs.drowned.jockey.only-babies", drownedJockeyOnlyBaby);
|
|
+ drownedJockeyChance = getDouble("mobs.drowned.jockey.chance", drownedJockeyChance);
|
|
+ drownedJockeyTryExistingChickens = getBoolean("mobs.drowned.jockey.try-existing-chickens", drownedJockeyTryExistingChickens);
|
|
+ drownedTakeDamageFromWater = getBoolean("mobs.drowned.takes-damage-from-water", drownedTakeDamageFromWater);
|
|
+ drownedBreakDoors = getBoolean("mobs.drowned.can-break-doors", drownedBreakDoors);
|
|
+ drownedAlwaysDropExp = getBoolean("mobs.drowned.always-drop-exp", drownedAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean elderGuardianRidable = false;
|
|
+ public boolean elderGuardianControllable = true;
|
|
+ public double elderGuardianMaxHealth = 80.0D;
|
|
+ public boolean elderGuardianTakeDamageFromWater = false;
|
|
+ public boolean elderGuardianAlwaysDropExp = false;
|
|
+ private void elderGuardianSettings() {
|
|
+ elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable);
|
|
+ elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.elder_guardian.attributes.max-health", elderGuardianMaxHealth);
|
|
+ set("mobs.elder_guardian.attributes.max-health", null);
|
|
+ set("mobs.elder_guardian.attributes.max_health", oldValue);
|
|
+ }
|
|
+ elderGuardianMaxHealth = getDouble("mobs.elder_guardian.attributes.max_health", elderGuardianMaxHealth);
|
|
+ elderGuardianTakeDamageFromWater = getBoolean("mobs.elder_guardian.takes-damage-from-water", elderGuardianTakeDamageFromWater);
|
|
+ elderGuardianAlwaysDropExp = getBoolean("mobs.elder_guardian.always-drop-exp", elderGuardianAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean enchantmentTableLapisPersists = false;
|
|
+ private void enchantmentTableSettings() {
|
|
+ enchantmentTableLapisPersists = getBoolean("blocks.enchantment-table.lapis-persists", enchantmentTableLapisPersists);
|
|
+ }
|
|
+
|
|
+ public boolean enderDragonRidable = false;
|
|
+ public boolean enderDragonRidableInWater = true;
|
|
+ public boolean enderDragonControllable = true;
|
|
+ public double enderDragonMaxY = 320D;
|
|
+ public double enderDragonMaxHealth = 200.0D;
|
|
+ public boolean enderDragonAlwaysDropsFullExp = false;
|
|
+ public boolean enderDragonBypassMobGriefing = false;
|
|
+ public boolean enderDragonTakeDamageFromWater = false;
|
|
+ public boolean enderDragonCanRideVehicles = false;
|
|
+ private void enderDragonSettings() {
|
|
+ enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable);
|
|
+ enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater);
|
|
+ enderDragonControllable = getBoolean("mobs.ender_dragon.controllable", enderDragonControllable);
|
|
+ enderDragonMaxY = getDouble("mobs.ender_dragon.ridable-max-y", enderDragonMaxY);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.ender_dragon.max-health", enderDragonMaxHealth);
|
|
+ set("mobs.ender_dragon.max-health", null);
|
|
+ set("mobs.ender_dragon.attributes.max_health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ender_dragon.attributes.max-health", enderDragonMaxHealth);
|
|
+ set("mobs.ender_dragon.attributes.max-health", null);
|
|
+ set("mobs.ender_dragon.attributes.max_health", oldValue);
|
|
+ }
|
|
+ enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth);
|
|
+ enderDragonAlwaysDropsFullExp = getBoolean("mobs.ender_dragon.always-drop-full-exp", enderDragonAlwaysDropsFullExp);
|
|
+ enderDragonBypassMobGriefing = getBoolean("mobs.ender_dragon.bypass-mob-griefing", enderDragonBypassMobGriefing);
|
|
+ enderDragonTakeDamageFromWater = getBoolean("mobs.ender_dragon.takes-damage-from-water", enderDragonTakeDamageFromWater);
|
|
+ enderDragonCanRideVehicles = getBoolean("mobs.ender_dragon.can-ride-vehicles", enderDragonCanRideVehicles);
|
|
+ }
|
|
+
|
|
+ public boolean endermanRidable = false;
|
|
+ public boolean endermanRidableInWater = true;
|
|
+ public boolean endermanControllable = true;
|
|
+ public double endermanMaxHealth = 40.0D;
|
|
+ public boolean endermanAllowGriefing = true;
|
|
+ public boolean endermanDespawnEvenWithBlock = false;
|
|
+ public boolean endermanBypassMobGriefing = false;
|
|
+ public boolean endermanTakeDamageFromWater = true;
|
|
+ public boolean endermanAggroEndermites = true;
|
|
+ public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = false;
|
|
+ public boolean endermanIgnorePlayerDragonHead = false;
|
|
+ public boolean endermanDisableStareAggro = false;
|
|
+ public boolean endermanIgnoreProjectiles = false;
|
|
+ public boolean endermanAlwaysDropExp = false;
|
|
+ private void endermanSettings() {
|
|
+ endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable);
|
|
+ endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater);
|
|
+ endermanControllable = getBoolean("mobs.enderman.controllable", endermanControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.enderman.attributes.max-health", endermanMaxHealth);
|
|
+ set("mobs.enderman.attributes.max-health", null);
|
|
+ set("mobs.enderman.attributes.max_health", oldValue);
|
|
+ }
|
|
+ if (PurpurConfig.version < 15) {
|
|
+ // remove old option
|
|
+ set("mobs.enderman.aggressive-towards-spawned-endermites", null);
|
|
+ }
|
|
+ endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth);
|
|
+ endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing);
|
|
+ endermanDespawnEvenWithBlock = getBoolean("mobs.enderman.can-despawn-with-held-block", endermanDespawnEvenWithBlock);
|
|
+ endermanBypassMobGriefing = getBoolean("mobs.enderman.bypass-mob-griefing", endermanBypassMobGriefing);
|
|
+ endermanTakeDamageFromWater = getBoolean("mobs.enderman.takes-damage-from-water", endermanTakeDamageFromWater);
|
|
+ endermanAggroEndermites = getBoolean("mobs.enderman.aggressive-towards-endermites", endermanAggroEndermites);
|
|
+ endermanAggroEndermitesOnlyIfPlayerSpawned = getBoolean("mobs.enderman.aggressive-towards-endermites-only-spawned-by-player-thrown-ender-pearls", endermanAggroEndermitesOnlyIfPlayerSpawned);
|
|
+ endermanIgnorePlayerDragonHead = getBoolean("mobs.enderman.ignore-players-wearing-dragon-head", endermanIgnorePlayerDragonHead);
|
|
+ endermanDisableStareAggro = getBoolean("mobs.enderman.disable-player-stare-aggression", endermanDisableStareAggro);
|
|
+ endermanIgnoreProjectiles = getBoolean("mobs.enderman.ignore-projectiles", endermanIgnoreProjectiles);
|
|
+ endermanAlwaysDropExp = getBoolean("mobs.enderman.always-drop-exp", endermanAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean endermiteRidable = false;
|
|
+ public boolean endermiteRidableInWater = true;
|
|
+ public boolean endermiteControllable = true;
|
|
+ public double endermiteMaxHealth = 8.0D;
|
|
+ public boolean endermiteTakeDamageFromWater = false;
|
|
+ public boolean endermiteAlwaysDropExp = false;
|
|
+ private void endermiteSettings() {
|
|
+ endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable);
|
|
+ endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater);
|
|
+ endermiteControllable = getBoolean("mobs.endermite.controllable", endermiteControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.endermite.attributes.max-health", endermiteMaxHealth);
|
|
+ set("mobs.endermite.attributes.max-health", null);
|
|
+ set("mobs.endermite.attributes.max_health", oldValue);
|
|
+ }
|
|
+ endermiteMaxHealth = getDouble("mobs.endermite.attributes.max_health", endermiteMaxHealth);
|
|
+ endermiteTakeDamageFromWater = getBoolean("mobs.endermite.takes-damage-from-water", endermiteTakeDamageFromWater);
|
|
+ endermiteAlwaysDropExp = getBoolean("mobs.endermite.always-drop-exp", endermiteAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean evokerRidable = false;
|
|
+ public boolean evokerRidableInWater = true;
|
|
+ public boolean evokerControllable = true;
|
|
+ public double evokerMaxHealth = 24.0D;
|
|
+ public boolean evokerBypassMobGriefing = false;
|
|
+ public boolean evokerTakeDamageFromWater = false;
|
|
+ public boolean evokerAlwaysDropExp = false;
|
|
+ private void evokerSettings() {
|
|
+ evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable);
|
|
+ evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater);
|
|
+ evokerControllable = getBoolean("mobs.evoker.controllable", evokerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.evoker.attributes.max-health", evokerMaxHealth);
|
|
+ set("mobs.evoker.attributes.max-health", null);
|
|
+ set("mobs.evoker.attributes.max_health", oldValue);
|
|
+ }
|
|
+ evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth);
|
|
+ evokerBypassMobGriefing = getBoolean("mobs.evoker.bypass-mob-griefing", evokerBypassMobGriefing);
|
|
+ evokerTakeDamageFromWater = getBoolean("mobs.evoker.takes-damage-from-water", evokerTakeDamageFromWater);
|
|
+ evokerAlwaysDropExp = getBoolean("mobs.evoker.always-drop-exp", evokerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean foxRidable = false;
|
|
+ public boolean foxRidableInWater = true;
|
|
+ public boolean foxControllable = true;
|
|
+ public double foxMaxHealth = 10.0D;
|
|
+ public boolean foxTypeChangesWithTulips = false;
|
|
+ public int foxBreedingTicks = 6000;
|
|
+ public boolean foxBypassMobGriefing = false;
|
|
+ public boolean foxTakeDamageFromWater = false;
|
|
+ public boolean foxAlwaysDropExp = false;
|
|
+ private void foxSettings() {
|
|
+ foxRidable = getBoolean("mobs.fox.ridable", foxRidable);
|
|
+ foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater);
|
|
+ foxControllable = getBoolean("mobs.fox.controllable", foxControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.fox.attributes.max-health", foxMaxHealth);
|
|
+ set("mobs.fox.attributes.max-health", null);
|
|
+ set("mobs.fox.attributes.max_health", oldValue);
|
|
+ }
|
|
+ foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth);
|
|
+ foxTypeChangesWithTulips = getBoolean("mobs.fox.tulips-change-type", foxTypeChangesWithTulips);
|
|
+ foxBreedingTicks = getInt("mobs.fox.breeding-delay-ticks", foxBreedingTicks);
|
|
+ foxBypassMobGriefing = getBoolean("mobs.fox.bypass-mob-griefing", foxBypassMobGriefing);
|
|
+ foxTakeDamageFromWater = getBoolean("mobs.fox.takes-damage-from-water", foxTakeDamageFromWater);
|
|
+ foxAlwaysDropExp = getBoolean("mobs.fox.always-drop-exp", foxAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean frogRidable = false;
|
|
+ public boolean frogRidableInWater = true;
|
|
+ public boolean frogControllable = true;
|
|
+ public float frogRidableJumpHeight = 0.65F;
|
|
+ public int frogBreedingTicks = 6000;
|
|
+ private void frogSettings() {
|
|
+ frogRidable = getBoolean("mobs.frog.ridable", frogRidable);
|
|
+ frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater);
|
|
+ frogControllable = getBoolean("mobs.frog.controllable", frogControllable);
|
|
+ frogRidableJumpHeight = (float) getDouble("mobs.frog.ridable-jump-height", frogRidableJumpHeight);
|
|
+ frogBreedingTicks = getInt("mobs.frog.breeding-delay-ticks", frogBreedingTicks);
|
|
+ }
|
|
+
|
|
+ public boolean ghastRidable = false;
|
|
+ public boolean ghastRidableInWater = true;
|
|
+ public boolean ghastControllable = true;
|
|
+ public double ghastMaxY = 320D;
|
|
+ public double ghastMaxHealth = 10.0D;
|
|
+ public boolean ghastTakeDamageFromWater = false;
|
|
+ public boolean ghastAlwaysDropExp = false;
|
|
+ private void ghastSettings() {
|
|
+ ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable);
|
|
+ ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater);
|
|
+ ghastControllable = getBoolean("mobs.ghast.controllable", ghastControllable);
|
|
+ ghastMaxY = getDouble("mobs.ghast.ridable-max-y", ghastMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ghast.attributes.max-health", ghastMaxHealth);
|
|
+ set("mobs.ghast.attributes.max-health", null);
|
|
+ set("mobs.ghast.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ghastMaxHealth = getDouble("mobs.ghast.attributes.max_health", ghastMaxHealth);
|
|
+ ghastTakeDamageFromWater = getBoolean("mobs.ghast.takes-damage-from-water", ghastTakeDamageFromWater);
|
|
+ ghastAlwaysDropExp = getBoolean("mobs.ghast.always-drop-exp", ghastAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean giantRidable = false;
|
|
+ public boolean giantRidableInWater = true;
|
|
+ public boolean giantControllable = true;
|
|
+ public double giantMovementSpeed = 0.5D;
|
|
+ public double giantAttackDamage = 50.0D;
|
|
+ public double giantMaxHealth = 100.0D;
|
|
+ public float giantStepHeight = 2.0F;
|
|
+ public float giantJumpHeight = 1.0F;
|
|
+ public boolean giantHaveAI = false;
|
|
+ public boolean giantHaveHostileAI = false;
|
|
+ public boolean giantTakeDamageFromWater = false;
|
|
+ public boolean giantAlwaysDropExp = false;
|
|
+ private void giantSettings() {
|
|
+ giantRidable = getBoolean("mobs.giant.ridable", giantRidable);
|
|
+ giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater);
|
|
+ giantControllable = getBoolean("mobs.giant.controllable", giantControllable);
|
|
+ giantMovementSpeed = getDouble("mobs.giant.movement-speed", giantMovementSpeed);
|
|
+ giantAttackDamage = getDouble("mobs.giant.attack-damage", giantAttackDamage);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.giant.max-health", giantMaxHealth);
|
|
+ set("mobs.giant.max-health", null);
|
|
+ set("mobs.giant.attributes.max_health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.giant.attributes.max-health", giantMaxHealth);
|
|
+ set("mobs.giant.attributes.max-health", null);
|
|
+ set("mobs.giant.attributes.max_health", oldValue);
|
|
+ }
|
|
+ giantMaxHealth = getDouble("mobs.giant.attributes.max_health", giantMaxHealth);
|
|
+ giantStepHeight = (float) getDouble("mobs.giant.step-height", giantStepHeight);
|
|
+ giantJumpHeight = (float) getDouble("mobs.giant.jump-height", giantJumpHeight);
|
|
+ giantHaveAI = getBoolean("mobs.giant.have-ai", giantHaveAI);
|
|
+ giantHaveHostileAI = getBoolean("mobs.giant.have-hostile-ai", giantHaveHostileAI);
|
|
+ giantTakeDamageFromWater = getBoolean("mobs.giant.takes-damage-from-water", giantTakeDamageFromWater);
|
|
+ giantAlwaysDropExp = getBoolean("mobs.giant.always-drop-exp", giantAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean glowSquidRidable = false;
|
|
+ public boolean glowSquidControllable = true;
|
|
+ public double glowSquidMaxHealth = 10.0D;
|
|
+ public boolean glowSquidsCanFly = false;
|
|
+ public boolean glowSquidTakeDamageFromWater = false;
|
|
+ public boolean glowSquidAlwaysDropExp = false;
|
|
+ public GlowSquidColor.Mode glowSquidColorMode = GlowSquidColor.Mode.RAINBOW;
|
|
+ private void glowSquidSettings() {
|
|
+ glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable);
|
|
+ glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable);
|
|
+ glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth);
|
|
+ glowSquidsCanFly = getBoolean("mobs.glow_squid.can-fly", glowSquidsCanFly);
|
|
+ glowSquidTakeDamageFromWater = getBoolean("mobs.glow_squid.takes-damage-from-water", glowSquidTakeDamageFromWater);
|
|
+ glowSquidAlwaysDropExp = getBoolean("mobs.glow_squid.always-drop-exp", glowSquidAlwaysDropExp);
|
|
+ glowSquidColorMode = GlowSquidColor.Mode.get(getString("mobs.glow_squid.rainglow-mode", glowSquidColorMode.toString()));
|
|
+ }
|
|
+
|
|
+ public boolean goatRidable = false;
|
|
+ public boolean goatRidableInWater = true;
|
|
+ public boolean goatControllable = true;
|
|
+ public double goatMaxHealth = 10.0D;
|
|
+ public int goatBreedingTicks = 6000;
|
|
+ public boolean goatTakeDamageFromWater = false;
|
|
+ public boolean goatAlwaysDropExp = false;
|
|
+ private void goatSettings() {
|
|
+ goatRidable = getBoolean("mobs.goat.ridable", goatRidable);
|
|
+ goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater);
|
|
+ goatControllable = getBoolean("mobs.goat.controllable", goatControllable);
|
|
+ goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth);
|
|
+ goatBreedingTicks = getInt("mobs.goat.breeding-delay-ticks", goatBreedingTicks);
|
|
+ goatTakeDamageFromWater = getBoolean("mobs.goat.takes-damage-from-water", goatTakeDamageFromWater);
|
|
+ goatAlwaysDropExp = getBoolean("mobs.goat.always-drop-exp", goatAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean guardianRidable = false;
|
|
+ public boolean guardianControllable = true;
|
|
+ public double guardianMaxHealth = 30.0D;
|
|
+ public boolean guardianTakeDamageFromWater = false;
|
|
+ public boolean guardianAlwaysDropExp = false;
|
|
+ private void guardianSettings() {
|
|
+ guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable);
|
|
+ guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.guardian.attributes.max-health", guardianMaxHealth);
|
|
+ set("mobs.guardian.attributes.max-health", null);
|
|
+ set("mobs.guardian.attributes.max_health", oldValue);
|
|
+ }
|
|
+ guardianMaxHealth = getDouble("mobs.guardian.attributes.max_health", guardianMaxHealth);
|
|
+ guardianTakeDamageFromWater = getBoolean("mobs.guardian.takes-damage-from-water", guardianTakeDamageFromWater);
|
|
+ guardianAlwaysDropExp = getBoolean("mobs.guardian.always-drop-exp", guardianAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean forceHalloweenSeason = false;
|
|
+ public float chanceHeadHalloweenOnEntity = 0.25F;
|
|
+ private void halloweenSetting() {
|
|
+ forceHalloweenSeason = getBoolean("gameplay-mechanics.halloween.force", forceHalloweenSeason);
|
|
+ chanceHeadHalloweenOnEntity = (float) getDouble("gameplay-mechanics.halloween.head-chance", chanceHeadHalloweenOnEntity);
|
|
+ }
|
|
+
|
|
+ public boolean hoglinRidable = false;
|
|
+ public boolean hoglinRidableInWater = true;
|
|
+ public boolean hoglinControllable = true;
|
|
+ public double hoglinMaxHealth = 40.0D;
|
|
+ public int hoglinBreedingTicks = 6000;
|
|
+ public boolean hoglinTakeDamageFromWater = false;
|
|
+ public boolean hoglinAlwaysDropExp = false;
|
|
+ private void hoglinSettings() {
|
|
+ hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable);
|
|
+ hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater);
|
|
+ hoglinControllable = getBoolean("mobs.hoglin.controllable", hoglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.hoglin.attributes.max-health", hoglinMaxHealth);
|
|
+ set("mobs.hoglin.attributes.max-health", null);
|
|
+ set("mobs.hoglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth);
|
|
+ hoglinBreedingTicks = getInt("mobs.hoglin.breeding-delay-ticks", hoglinBreedingTicks);
|
|
+ hoglinTakeDamageFromWater = getBoolean("mobs.hoglin.takes-damage-from-water", hoglinTakeDamageFromWater);
|
|
+ hoglinAlwaysDropExp = getBoolean("mobs.hoglin.always-drop-exp", hoglinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean horseRidableInWater = false;
|
|
+ public double horseMaxHealthMin = 15.0D;
|
|
+ public double horseMaxHealthMax = 30.0D;
|
|
+ public double horseJumpStrengthMin = 0.4D;
|
|
+ public double horseJumpStrengthMax = 1.0D;
|
|
+ public double horseMovementSpeedMin = 0.1125D;
|
|
+ public double horseMovementSpeedMax = 0.3375D;
|
|
+ public int horseBreedingTicks = 6000;
|
|
+ public boolean horseTakeDamageFromWater = false;
|
|
+ public boolean horseAlwaysDropExp = false;
|
|
+ private void horseSettings() {
|
|
+ horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.horse.attributes.max-health.min", horseMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.horse.attributes.max-health.max", horseMaxHealthMax);
|
|
+ set("mobs.horse.attributes.max-health", null);
|
|
+ set("mobs.horse.attributes.max_health.min", oldMin);
|
|
+ set("mobs.horse.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ horseMaxHealthMin = getDouble("mobs.horse.attributes.max_health.min", horseMaxHealthMin);
|
|
+ horseMaxHealthMax = getDouble("mobs.horse.attributes.max_health.max", horseMaxHealthMax);
|
|
+ horseJumpStrengthMin = getDouble("mobs.horse.attributes.jump_strength.min", horseJumpStrengthMin);
|
|
+ horseJumpStrengthMax = getDouble("mobs.horse.attributes.jump_strength.max", horseJumpStrengthMax);
|
|
+ horseMovementSpeedMin = getDouble("mobs.horse.attributes.movement_speed.min", horseMovementSpeedMin);
|
|
+ horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax);
|
|
+ horseBreedingTicks = getInt("mobs.horse.breeding-delay-ticks", horseBreedingTicks);
|
|
+ horseTakeDamageFromWater = getBoolean("mobs.horse.takes-damage-from-water", horseTakeDamageFromWater);
|
|
+ horseAlwaysDropExp = getBoolean("mobs.horse.always-drop-exp", horseAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean huskRidable = false;
|
|
+ public boolean huskRidableInWater = true;
|
|
+ public boolean huskControllable = true;
|
|
+ public double huskMaxHealth = 20.0D;
|
|
+ public double huskSpawnReinforcements = 0.1D;
|
|
+ public boolean huskJockeyOnlyBaby = true;
|
|
+ public double huskJockeyChance = 0.05D;
|
|
+ public boolean huskJockeyTryExistingChickens = true;
|
|
+ public boolean huskTakeDamageFromWater = false;
|
|
+ public boolean huskAlwaysDropExp = false;
|
|
+ private void huskSettings() {
|
|
+ huskRidable = getBoolean("mobs.husk.ridable", huskRidable);
|
|
+ huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater);
|
|
+ huskControllable = getBoolean("mobs.husk.controllable", huskControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.husk.attributes.max-health", huskMaxHealth);
|
|
+ set("mobs.husk.attributes.max-health", null);
|
|
+ set("mobs.husk.attributes.max_health", oldValue);
|
|
+ }
|
|
+ huskMaxHealth = getDouble("mobs.husk.attributes.max_health", huskMaxHealth);
|
|
+ huskSpawnReinforcements = getDouble("mobs.husk.attributes.spawn_reinforcements", huskSpawnReinforcements);
|
|
+ huskJockeyOnlyBaby = getBoolean("mobs.husk.jockey.only-babies", huskJockeyOnlyBaby);
|
|
+ huskJockeyChance = getDouble("mobs.husk.jockey.chance", huskJockeyChance);
|
|
+ huskJockeyTryExistingChickens = getBoolean("mobs.husk.jockey.try-existing-chickens", huskJockeyTryExistingChickens);
|
|
+ huskTakeDamageFromWater = getBoolean("mobs.husk.takes-damage-from-water", huskTakeDamageFromWater);
|
|
+ huskAlwaysDropExp = getBoolean("mobs.husk.always-drop-exp", huskAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean illusionerRidable = false;
|
|
+ public boolean illusionerRidableInWater = true;
|
|
+ public boolean illusionerControllable = true;
|
|
+ public double illusionerMovementSpeed = 0.5D;
|
|
+ public double illusionerFollowRange = 18.0D;
|
|
+ public double illusionerMaxHealth = 32.0D;
|
|
+ public boolean illusionerTakeDamageFromWater = false;
|
|
+ public boolean illusionerAlwaysDropExp = false;
|
|
+ private void illusionerSettings() {
|
|
+ illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable);
|
|
+ illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater);
|
|
+ illusionerControllable = getBoolean("mobs.illusioner.controllable", illusionerControllable);
|
|
+ illusionerMovementSpeed = getDouble("mobs.illusioner.movement-speed", illusionerMovementSpeed);
|
|
+ illusionerFollowRange = getDouble("mobs.illusioner.follow-range", illusionerFollowRange);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.illusioner.max-health", illusionerMaxHealth);
|
|
+ set("mobs.illusioner.max-health", null);
|
|
+ set("mobs.illusioner.attributes.max_health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.illusioner.attributes.max-health", illusionerMaxHealth);
|
|
+ set("mobs.illusioner.attributes.max-health", null);
|
|
+ set("mobs.illusioner.attributes.max_health", oldValue);
|
|
+ }
|
|
+ illusionerMaxHealth = getDouble("mobs.illusioner.attributes.max_health", illusionerMaxHealth);
|
|
+ illusionerTakeDamageFromWater = getBoolean("mobs.illusioner.takes-damage-from-water", illusionerTakeDamageFromWater);
|
|
+ illusionerAlwaysDropExp = getBoolean("mobs.illusioner.always-drop-exp", illusionerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean ironGolemRidable = false;
|
|
+ public boolean ironGolemRidableInWater = true;
|
|
+ public boolean ironGolemControllable = true;
|
|
+ public boolean ironGolemCanSwim = false;
|
|
+ public double ironGolemMaxHealth = 100.0D;
|
|
+ public boolean ironGolemTakeDamageFromWater = false;
|
|
+ public boolean ironGolemPoppyCalm = false;
|
|
+ public boolean ironGolemHealCalm = false;
|
|
+ public boolean ironGolemAlwaysDropExp = false;
|
|
+ private void ironGolemSettings() {
|
|
+ ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable);
|
|
+ ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater);
|
|
+ ironGolemControllable = getBoolean("mobs.iron_golem.controllable", ironGolemControllable);
|
|
+ ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.iron_golem.attributes.max-health", ironGolemMaxHealth);
|
|
+ set("mobs.iron_golem.attributes.max-health", null);
|
|
+ set("mobs.iron_golem.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ironGolemMaxHealth = getDouble("mobs.iron_golem.attributes.max_health", ironGolemMaxHealth);
|
|
+ ironGolemTakeDamageFromWater = getBoolean("mobs.iron_golem.takes-damage-from-water", ironGolemTakeDamageFromWater);
|
|
+ ironGolemPoppyCalm = getBoolean("mobs.iron_golem.poppy-calms-anger", ironGolemPoppyCalm);
|
|
+ ironGolemHealCalm = getBoolean("mobs.iron_golem.healing-calms-anger", ironGolemHealCalm);
|
|
+ ironGolemAlwaysDropExp = getBoolean("mobs.iron_golem.always-drop-exp", ironGolemAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean llamaRidable = false;
|
|
+ public boolean llamaRidableInWater = false;
|
|
+ public boolean llamaControllable = true;
|
|
+ public double llamaMaxHealthMin = 15.0D;
|
|
+ public double llamaMaxHealthMax = 30.0D;
|
|
+ public double llamaJumpStrengthMin = 0.5D;
|
|
+ public double llamaJumpStrengthMax = 0.5D;
|
|
+ public double llamaMovementSpeedMin = 0.175D;
|
|
+ public double llamaMovementSpeedMax = 0.175D;
|
|
+ public int llamaBreedingTicks = 6000;
|
|
+ public boolean llamaTakeDamageFromWater = false;
|
|
+ public boolean llamaJoinCaravans = true;
|
|
+ public boolean llamaAlwaysDropExp = false;
|
|
+ private void llamaSettings() {
|
|
+ llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable);
|
|
+ llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater);
|
|
+ llamaControllable = getBoolean("mobs.llama.controllable", llamaControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.llama.attributes.max-health.min", llamaMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.llama.attributes.max-health.max", llamaMaxHealthMax);
|
|
+ set("mobs.llama.attributes.max-health", null);
|
|
+ set("mobs.llama.attributes.max_health.min", oldMin);
|
|
+ set("mobs.llama.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ llamaMaxHealthMin = getDouble("mobs.llama.attributes.max_health.min", llamaMaxHealthMin);
|
|
+ llamaMaxHealthMax = getDouble("mobs.llama.attributes.max_health.max", llamaMaxHealthMax);
|
|
+ llamaJumpStrengthMin = getDouble("mobs.llama.attributes.jump_strength.min", llamaJumpStrengthMin);
|
|
+ llamaJumpStrengthMax = getDouble("mobs.llama.attributes.jump_strength.max", llamaJumpStrengthMax);
|
|
+ llamaMovementSpeedMin = getDouble("mobs.llama.attributes.movement_speed.min", llamaMovementSpeedMin);
|
|
+ llamaMovementSpeedMax = getDouble("mobs.llama.attributes.movement_speed.max", llamaMovementSpeedMax);
|
|
+ llamaBreedingTicks = getInt("mobs.llama.breeding-delay-ticks", llamaBreedingTicks);
|
|
+ llamaTakeDamageFromWater = getBoolean("mobs.llama.takes-damage-from-water", llamaTakeDamageFromWater);
|
|
+ llamaJoinCaravans = getBoolean("mobs.llama.join-caravans", llamaJoinCaravans);
|
|
+ llamaAlwaysDropExp = getBoolean("mobs.llama.always-drop-exp", llamaAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean magmaCubeRidable = false;
|
|
+ public boolean magmaCubeRidableInWater = true;
|
|
+ public boolean magmaCubeControllable = true;
|
|
+ public String magmaCubeMaxHealth = "size * size";
|
|
+ public String magmaCubeAttackDamage = "size";
|
|
+ public Map<Integer, Double> magmaCubeMaxHealthCache = new HashMap<>();
|
|
+ public Map<Integer, Double> magmaCubeAttackDamageCache = new HashMap<>();
|
|
+ public boolean magmaCubeTakeDamageFromWater = false;
|
|
+ public boolean magmaCubeAlwaysDropExp = false;
|
|
+ private void magmaCubeSettings() {
|
|
+ magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable);
|
|
+ magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater);
|
|
+ magmaCubeControllable = getBoolean("mobs.magma_cube.controllable", magmaCubeControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ String oldValue = getString("mobs.magma_cube.attributes.max-health", magmaCubeMaxHealth);
|
|
+ set("mobs.magma_cube.attributes.max-health", null);
|
|
+ set("mobs.magma_cube.attributes.max_health", oldValue);
|
|
+ }
|
|
+ magmaCubeMaxHealth = getString("mobs.magma_cube.attributes.max_health", magmaCubeMaxHealth);
|
|
+ magmaCubeAttackDamage = getString("mobs.magma_cube.attributes.attack_damage", magmaCubeAttackDamage);
|
|
+ magmaCubeMaxHealthCache.clear();
|
|
+ magmaCubeAttackDamageCache.clear();
|
|
+ magmaCubeTakeDamageFromWater = getBoolean("mobs.magma_cube.takes-damage-from-water", magmaCubeTakeDamageFromWater);
|
|
+ magmaCubeAlwaysDropExp = getBoolean("mobs.magma_cube.always-drop-exp", magmaCubeAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean mooshroomRidable = false;
|
|
+ public boolean mooshroomRidableInWater = true;
|
|
+ public boolean mooshroomControllable = true;
|
|
+ public double mooshroomMaxHealth = 10.0D;
|
|
+ public int mooshroomBreedingTicks = 6000;
|
|
+ public boolean mooshroomTakeDamageFromWater = false;
|
|
+ public boolean mooshroomAlwaysDropExp = false;
|
|
+ private void mooshroomSettings() {
|
|
+ mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable);
|
|
+ mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater);
|
|
+ mooshroomControllable = getBoolean("mobs.mooshroom.controllable", mooshroomControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.mooshroom.attributes.max-health", mooshroomMaxHealth);
|
|
+ set("mobs.mooshroom.attributes.max-health", null);
|
|
+ set("mobs.mooshroom.attributes.max_health", oldValue);
|
|
+ }
|
|
+ mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth);
|
|
+ mooshroomBreedingTicks = getInt("mobs.mooshroom.breeding-delay-ticks", mooshroomBreedingTicks);
|
|
+ mooshroomTakeDamageFromWater = getBoolean("mobs.mooshroom.takes-damage-from-water", mooshroomTakeDamageFromWater);
|
|
+ mooshroomAlwaysDropExp = getBoolean("mobs.mooshroom.always-drop-exp", mooshroomAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean muleRidableInWater = false;
|
|
+ public double muleMaxHealthMin = 15.0D;
|
|
+ public double muleMaxHealthMax = 30.0D;
|
|
+ public double muleJumpStrengthMin = 0.5D;
|
|
+ public double muleJumpStrengthMax = 0.5D;
|
|
+ public double muleMovementSpeedMin = 0.175D;
|
|
+ public double muleMovementSpeedMax = 0.175D;
|
|
+ public int muleBreedingTicks = 6000;
|
|
+ public boolean muleTakeDamageFromWater = false;
|
|
+ public boolean muleAlwaysDropExp = false;
|
|
+ private void muleSettings() {
|
|
+ muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.mule.attributes.max-health.min", muleMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.mule.attributes.max-health.max", muleMaxHealthMax);
|
|
+ set("mobs.mule.attributes.max-health", null);
|
|
+ set("mobs.mule.attributes.max_health.min", oldMin);
|
|
+ set("mobs.mule.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ muleMaxHealthMin = getDouble("mobs.mule.attributes.max_health.min", muleMaxHealthMin);
|
|
+ muleMaxHealthMax = getDouble("mobs.mule.attributes.max_health.max", muleMaxHealthMax);
|
|
+ muleJumpStrengthMin = getDouble("mobs.mule.attributes.jump_strength.min", muleJumpStrengthMin);
|
|
+ muleJumpStrengthMax = getDouble("mobs.mule.attributes.jump_strength.max", muleJumpStrengthMax);
|
|
+ muleMovementSpeedMin = getDouble("mobs.mule.attributes.movement_speed.min", muleMovementSpeedMin);
|
|
+ muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax);
|
|
+ muleBreedingTicks = getInt("mobs.mule.breeding-delay-ticks", muleBreedingTicks);
|
|
+ muleTakeDamageFromWater = getBoolean("mobs.mule.takes-damage-from-water", muleTakeDamageFromWater);
|
|
+ muleAlwaysDropExp = getBoolean("mobs.mule.always-drop-exp", muleAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean ocelotRidable = false;
|
|
+ public boolean ocelotRidableInWater = true;
|
|
+ public boolean ocelotControllable = true;
|
|
+ public double ocelotMaxHealth = 10.0D;
|
|
+ public int ocelotBreedingTicks = 6000;
|
|
+ public boolean ocelotTakeDamageFromWater = false;
|
|
+ public boolean ocelotAlwaysDropExp = false;
|
|
+ private void ocelotSettings() {
|
|
+ ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable);
|
|
+ ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater);
|
|
+ ocelotControllable = getBoolean("mobs.ocelot.controllable", ocelotControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ocelot.attributes.max-health", ocelotMaxHealth);
|
|
+ set("mobs.ocelot.attributes.max-health", null);
|
|
+ set("mobs.ocelot.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth);
|
|
+ ocelotBreedingTicks = getInt("mobs.ocelot.breeding-delay-ticks", ocelotBreedingTicks);
|
|
+ ocelotTakeDamageFromWater = getBoolean("mobs.ocelot.takes-damage-from-water", ocelotTakeDamageFromWater);
|
|
+ ocelotAlwaysDropExp = getBoolean("mobs.ocelot.always-drop-exp", ocelotAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean pandaRidable = false;
|
|
+ public boolean pandaRidableInWater = true;
|
|
+ public boolean pandaControllable = true;
|
|
+ public double pandaMaxHealth = 20.0D;
|
|
+ public int pandaBreedingTicks = 6000;
|
|
+ public boolean pandaTakeDamageFromWater = false;
|
|
+ public boolean pandaAlwaysDropExp = false;
|
|
+ private void pandaSettings() {
|
|
+ pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable);
|
|
+ pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater);
|
|
+ pandaControllable = getBoolean("mobs.panda.controllable", pandaControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.panda.attributes.max-health", pandaMaxHealth);
|
|
+ set("mobs.panda.attributes.max-health", null);
|
|
+ set("mobs.panda.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth);
|
|
+ pandaBreedingTicks = getInt("mobs.panda.breeding-delay-ticks", pandaBreedingTicks);
|
|
+ pandaTakeDamageFromWater = getBoolean("mobs.panda.takes-damage-from-water", pandaTakeDamageFromWater);
|
|
+ pandaAlwaysDropExp = getBoolean("mobs.panda.always-drop-exp", pandaAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean parrotRidable = false;
|
|
+ public boolean parrotRidableInWater = true;
|
|
+ public boolean parrotControllable = true;
|
|
+ public double parrotMaxY = 320D;
|
|
+ public double parrotMaxHealth = 6.0D;
|
|
+ public boolean parrotTakeDamageFromWater = false;
|
|
+ public boolean parrotBreedable = false;
|
|
+ public boolean parrotAlwaysDropExp = false;
|
|
+ private void parrotSettings() {
|
|
+ parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable);
|
|
+ parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater);
|
|
+ parrotControllable = getBoolean("mobs.parrot.controllable", parrotControllable);
|
|
+ parrotMaxY = getDouble("mobs.parrot.ridable-max-y", parrotMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.parrot.attributes.max-health", parrotMaxHealth);
|
|
+ set("mobs.parrot.attributes.max-health", null);
|
|
+ set("mobs.parrot.attributes.max_health", oldValue);
|
|
+ }
|
|
+ parrotMaxHealth = getDouble("mobs.parrot.attributes.max_health", parrotMaxHealth);
|
|
+ parrotTakeDamageFromWater = getBoolean("mobs.parrot.takes-damage-from-water", parrotTakeDamageFromWater);
|
|
+ parrotBreedable = getBoolean("mobs.parrot.can-breed", parrotBreedable);
|
|
+ parrotAlwaysDropExp = getBoolean("mobs.parrot.always-drop-exp", parrotAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean phantomRidable = false;
|
|
+ public boolean phantomRidableInWater = true;
|
|
+ public boolean phantomControllable = true;
|
|
+ public double phantomMaxY = 320D;
|
|
+ public float phantomFlameDamage = 1.0F;
|
|
+ public int phantomFlameFireTime = 8;
|
|
+ public boolean phantomAllowGriefing = false;
|
|
+ public String phantomMaxHealth = "20.0";
|
|
+ public String phantomAttackDamage = "6 + size";
|
|
+ public Map<Integer, Double> phantomMaxHealthCache = new HashMap<>();
|
|
+ public Map<Integer, Double> phantomAttackDamageCache = new HashMap<>();
|
|
+ public double phantomAttackedByCrystalRadius = 0.0D;
|
|
+ public float phantomAttackedByCrystalDamage = 1.0F;
|
|
+ public double phantomOrbitCrystalRadius = 0.0D;
|
|
+ public int phantomSpawnMinSkyDarkness = 5;
|
|
+ public boolean phantomSpawnOnlyAboveSeaLevel = true;
|
|
+ public boolean phantomSpawnOnlyWithVisibleSky = true;
|
|
+ public double phantomSpawnLocalDifficultyChance = 3.0D;
|
|
+ public int phantomSpawnMinPerAttempt = 1;
|
|
+ public int phantomSpawnMaxPerAttempt = -1;
|
|
+ public int phantomBurnInLight = 0;
|
|
+ public boolean phantomIgnorePlayersWithTorch = false;
|
|
+ public boolean phantomBurnInDaylight = true;
|
|
+ public boolean phantomFlamesOnSwoop = false;
|
|
+ public boolean phantomTakeDamageFromWater = false;
|
|
+ public boolean phantomAlwaysDropExp = false;
|
|
+ public int phantomMinSize = 0;
|
|
+ public int phantomMaxSize = 0;
|
|
+ private void phantomSettings() {
|
|
+ phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable);
|
|
+ phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater);
|
|
+ phantomControllable = getBoolean("mobs.phantom.controllable", phantomControllable);
|
|
+ phantomMaxY = getDouble("mobs.phantom.ridable-max-y", phantomMaxY);
|
|
+ phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage);
|
|
+ phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime);
|
|
+ phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.phantom.attributes.max-health", Double.parseDouble(phantomMaxHealth));
|
|
+ set("mobs.phantom.attributes.max-health", null);
|
|
+ set("mobs.phantom.attributes.max_health", String.valueOf(oldValue));
|
|
+ }
|
|
+ if (PurpurConfig.version < 25) {
|
|
+ double oldValue = getDouble("mobs.phantom.attributes.max_health", Double.parseDouble(phantomMaxHealth));
|
|
+ set("mobs.phantom.attributes.max_health", String.valueOf(oldValue));
|
|
+ }
|
|
+ phantomMaxHealth = getString("mobs.phantom.attributes.max_health", phantomMaxHealth);
|
|
+ phantomAttackDamage = getString("mobs.phantom.attributes.attack_damage", phantomAttackDamage);
|
|
+ phantomMaxHealthCache.clear();
|
|
+ phantomAttackDamageCache.clear();
|
|
+ phantomAttackedByCrystalRadius = getDouble("mobs.phantom.attacked-by-crystal-range", phantomAttackedByCrystalRadius);
|
|
+ phantomAttackedByCrystalDamage = (float) getDouble("mobs.phantom.attacked-by-crystal-damage", phantomAttackedByCrystalDamage);
|
|
+ phantomOrbitCrystalRadius = getDouble("mobs.phantom.orbit-crystal-radius", phantomOrbitCrystalRadius);
|
|
+ phantomSpawnMinSkyDarkness = getInt("mobs.phantom.spawn.min-sky-darkness", phantomSpawnMinSkyDarkness);
|
|
+ phantomSpawnOnlyAboveSeaLevel = getBoolean("mobs.phantom.spawn.only-above-sea-level", phantomSpawnOnlyAboveSeaLevel);
|
|
+ phantomSpawnOnlyWithVisibleSky = getBoolean("mobs.phantom.spawn.only-with-visible-sky", phantomSpawnOnlyWithVisibleSky);
|
|
+ phantomSpawnLocalDifficultyChance = getDouble("mobs.phantom.spawn.local-difficulty-chance", phantomSpawnLocalDifficultyChance);
|
|
+ phantomSpawnMinPerAttempt = getInt("mobs.phantom.spawn.per-attempt.min", phantomSpawnMinPerAttempt);
|
|
+ phantomSpawnMaxPerAttempt = getInt("mobs.phantom.spawn.per-attempt.max", phantomSpawnMaxPerAttempt);
|
|
+ phantomBurnInLight = getInt("mobs.phantom.burn-in-light", phantomBurnInLight);
|
|
+ phantomBurnInDaylight = getBoolean("mobs.phantom.burn-in-daylight", phantomBurnInDaylight);
|
|
+ phantomIgnorePlayersWithTorch = getBoolean("mobs.phantom.ignore-players-with-torch", phantomIgnorePlayersWithTorch);
|
|
+ phantomFlamesOnSwoop = getBoolean("mobs.phantom.flames-on-swoop", phantomFlamesOnSwoop);
|
|
+ phantomTakeDamageFromWater = getBoolean("mobs.phantom.takes-damage-from-water", phantomTakeDamageFromWater);
|
|
+ phantomAlwaysDropExp = getBoolean("mobs.phantom.always-drop-exp", phantomAlwaysDropExp);
|
|
+ phantomMinSize = Mth.clamp(getInt("mobs.phantom.size.min", phantomMinSize), 0, 64);
|
|
+ phantomMaxSize = Mth.clamp(getInt("mobs.phantom.size.max", phantomMaxSize), 0, 64);
|
|
+ if (phantomMinSize > phantomMaxSize) {
|
|
+ phantomMinSize = phantomMinSize ^ phantomMaxSize;
|
|
+ phantomMaxSize = phantomMinSize ^ phantomMaxSize;
|
|
+ phantomMinSize = phantomMinSize ^ phantomMaxSize;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean pigRidable = false;
|
|
+ public boolean pigRidableInWater = false;
|
|
+ public boolean pigControllable = true;
|
|
+ public double pigMaxHealth = 10.0D;
|
|
+ public boolean pigGiveSaddleBack = false;
|
|
+ public int pigBreedingTicks = 6000;
|
|
+ public boolean pigTakeDamageFromWater = false;
|
|
+ public boolean pigAlwaysDropExp = false;
|
|
+ private void pigSettings() {
|
|
+ pigRidable = getBoolean("mobs.pig.ridable", pigRidable);
|
|
+ pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater);
|
|
+ pigControllable = getBoolean("mobs.pig.controllable", pigControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.pig.attributes.max-health", pigMaxHealth);
|
|
+ set("mobs.pig.attributes.max-health", null);
|
|
+ set("mobs.pig.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth);
|
|
+ pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack);
|
|
+ pigBreedingTicks = getInt("mobs.pig.breeding-delay-ticks", pigBreedingTicks);
|
|
+ pigTakeDamageFromWater = getBoolean("mobs.pig.takes-damage-from-water", pigTakeDamageFromWater);
|
|
+ pigAlwaysDropExp = getBoolean("mobs.pig.always-drop-exp", pigAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean piglinRidable = false;
|
|
+ public boolean piglinRidableInWater = true;
|
|
+ public boolean piglinControllable = true;
|
|
+ public double piglinMaxHealth = 16.0D;
|
|
+ public boolean piglinBypassMobGriefing = false;
|
|
+ public boolean piglinTakeDamageFromWater = false;
|
|
+ public int piglinPortalSpawnModifier = 2000;
|
|
+ public boolean piglinAlwaysDropExp = false;
|
|
+ public double piglinHeadVisibilityPercent = 0.5D;
|
|
+ private void piglinSettings() {
|
|
+ piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable);
|
|
+ piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater);
|
|
+ piglinControllable = getBoolean("mobs.piglin.controllable", piglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.piglin.attributes.max-health", piglinMaxHealth);
|
|
+ set("mobs.piglin.attributes.max-health", null);
|
|
+ set("mobs.piglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth);
|
|
+ piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing);
|
|
+ piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater);
|
|
+ piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier);
|
|
+ piglinAlwaysDropExp = getBoolean("mobs.piglin.always-drop-exp", piglinAlwaysDropExp);
|
|
+ piglinHeadVisibilityPercent = getDouble("mobs.piglin.head-visibility-percent", piglinHeadVisibilityPercent);
|
|
+ }
|
|
+
|
|
+ public boolean piglinBruteRidable = false;
|
|
+ public boolean piglinBruteRidableInWater = true;
|
|
+ public boolean piglinBruteControllable = true;
|
|
+ public double piglinBruteMaxHealth = 50.0D;
|
|
+ public boolean piglinBruteTakeDamageFromWater = false;
|
|
+ public boolean piglinBruteAlwaysDropExp = false;
|
|
+ private void piglinBruteSettings() {
|
|
+ piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable);
|
|
+ piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater);
|
|
+ piglinBruteControllable = getBoolean("mobs.piglin_brute.controllable", piglinBruteControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.piglin_brute.attributes.max-health", piglinBruteMaxHealth);
|
|
+ set("mobs.piglin_brute.attributes.max-health", null);
|
|
+ set("mobs.piglin_brute.attributes.max_health", oldValue);
|
|
+ }
|
|
+ piglinBruteMaxHealth = getDouble("mobs.piglin_brute.attributes.max_health", piglinBruteMaxHealth);
|
|
+ piglinBruteTakeDamageFromWater = getBoolean("mobs.piglin_brute.takes-damage-from-water", piglinBruteTakeDamageFromWater);
|
|
+ piglinBruteAlwaysDropExp = getBoolean("mobs.piglin_brute.always-drop-exp", piglinBruteAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean pillagerRidable = false;
|
|
+ public boolean pillagerRidableInWater = true;
|
|
+ public boolean pillagerControllable = true;
|
|
+ public double pillagerMaxHealth = 24.0D;
|
|
+ public boolean pillagerBypassMobGriefing = false;
|
|
+ public boolean pillagerTakeDamageFromWater = false;
|
|
+ public boolean pillagerAlwaysDropExp = false;
|
|
+ private void pillagerSettings() {
|
|
+ pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable);
|
|
+ pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater);
|
|
+ pillagerControllable = getBoolean("mobs.pillager.controllable", pillagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.pillager.attributes.max-health", pillagerMaxHealth);
|
|
+ set("mobs.pillager.attributes.max-health", null);
|
|
+ set("mobs.pillager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth);
|
|
+ pillagerBypassMobGriefing = getBoolean("mobs.pillager.bypass-mob-griefing", pillagerBypassMobGriefing);
|
|
+ pillagerTakeDamageFromWater = getBoolean("mobs.pillager.takes-damage-from-water", pillagerTakeDamageFromWater);
|
|
+ pillagerAlwaysDropExp = getBoolean("mobs.pillager.always-drop-exp", pillagerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean polarBearRidable = false;
|
|
+ public boolean polarBearRidableInWater = true;
|
|
+ public boolean polarBearControllable = true;
|
|
+ public double polarBearMaxHealth = 30.0D;
|
|
+ public String polarBearBreedableItemString = "";
|
|
+ public Item polarBearBreedableItem = null;
|
|
+ public int polarBearBreedingTicks = 6000;
|
|
+ public boolean polarBearTakeDamageFromWater = false;
|
|
+ public boolean polarBearAlwaysDropExp = false;
|
|
+ private void polarBearSettings() {
|
|
+ polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable);
|
|
+ polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater);
|
|
+ polarBearControllable = getBoolean("mobs.polar_bear.controllable", polarBearControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.polar_bear.attributes.max-health", polarBearMaxHealth);
|
|
+ set("mobs.polar_bear.attributes.max-health", null);
|
|
+ set("mobs.polar_bear.attributes.max_health", oldValue);
|
|
+ }
|
|
+ polarBearMaxHealth = getDouble("mobs.polar_bear.attributes.max_health", polarBearMaxHealth);
|
|
+ polarBearBreedableItemString = getString("mobs.polar_bear.breedable-item", polarBearBreedableItemString);
|
|
+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(polarBearBreedableItemString));
|
|
+ if (item != Items.AIR) polarBearBreedableItem = item;
|
|
+ polarBearBreedingTicks = getInt("mobs.polar_bear.breeding-delay-ticks", polarBearBreedingTicks);
|
|
+ polarBearTakeDamageFromWater = getBoolean("mobs.polar_bear.takes-damage-from-water", polarBearTakeDamageFromWater);
|
|
+ polarBearAlwaysDropExp = getBoolean("mobs.polar_bear.always-drop-exp", polarBearAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean pufferfishRidable = false;
|
|
+ public boolean pufferfishControllable = true;
|
|
+ public double pufferfishMaxHealth = 3.0D;
|
|
+ public boolean pufferfishTakeDamageFromWater = false;
|
|
+ public boolean pufferfishAlwaysDropExp = false;
|
|
+ private void pufferfishSettings() {
|
|
+ pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable);
|
|
+ pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.pufferfish.attributes.max-health", pufferfishMaxHealth);
|
|
+ set("mobs.pufferfish.attributes.max-health", null);
|
|
+ set("mobs.pufferfish.attributes.max_health", oldValue);
|
|
+ }
|
|
+ pufferfishMaxHealth = getDouble("mobs.pufferfish.attributes.max_health", pufferfishMaxHealth);
|
|
+ pufferfishTakeDamageFromWater = getBoolean("mobs.pufferfish.takes-damage-from-water", pufferfishTakeDamageFromWater);
|
|
+ pufferfishAlwaysDropExp = getBoolean("mobs.pufferfish.always-drop-exp", pufferfishAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean rabbitRidable = false;
|
|
+ public boolean rabbitRidableInWater = true;
|
|
+ public boolean rabbitControllable = true;
|
|
+ public double rabbitMaxHealth = 3.0D;
|
|
+ public double rabbitNaturalToast = 0.0D;
|
|
+ public double rabbitNaturalKiller = 0.0D;
|
|
+ public int rabbitBreedingTicks = 6000;
|
|
+ public boolean rabbitBypassMobGriefing = false;
|
|
+ public boolean rabbitTakeDamageFromWater = false;
|
|
+ public boolean rabbitAlwaysDropExp = false;
|
|
+ private void rabbitSettings() {
|
|
+ rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable);
|
|
+ rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater);
|
|
+ rabbitControllable = getBoolean("mobs.rabbit.controllable", rabbitControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.rabbit.attributes.max-health", rabbitMaxHealth);
|
|
+ set("mobs.rabbit.attributes.max-health", null);
|
|
+ set("mobs.rabbit.attributes.max_health", oldValue);
|
|
+ }
|
|
+ rabbitMaxHealth = getDouble("mobs.rabbit.attributes.max_health", rabbitMaxHealth);
|
|
+ rabbitNaturalToast = getDouble("mobs.rabbit.spawn-toast-chance", rabbitNaturalToast);
|
|
+ rabbitNaturalKiller = getDouble("mobs.rabbit.spawn-killer-rabbit-chance", rabbitNaturalKiller);
|
|
+ rabbitBreedingTicks = getInt("mobs.rabbit.breeding-delay-ticks", rabbitBreedingTicks);
|
|
+ rabbitBypassMobGriefing = getBoolean("mobs.rabbit.bypass-mob-griefing", rabbitBypassMobGriefing);
|
|
+ rabbitTakeDamageFromWater = getBoolean("mobs.rabbit.takes-damage-from-water", rabbitTakeDamageFromWater);
|
|
+ rabbitAlwaysDropExp = getBoolean("mobs.rabbit.always-drop-exp", rabbitAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean ravagerRidable = false;
|
|
+ public boolean ravagerRidableInWater = false;
|
|
+ public boolean ravagerControllable = true;
|
|
+ public double ravagerMaxHealth = 100.0D;
|
|
+ public boolean ravagerBypassMobGriefing = false;
|
|
+ public boolean ravagerTakeDamageFromWater = false;
|
|
+ public List<Block> ravagerGriefableBlocks = new ArrayList<>();
|
|
+ public boolean ravagerAlwaysDropExp = false;
|
|
+ private void ravagerSettings() {
|
|
+ ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable);
|
|
+ ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater);
|
|
+ ravagerControllable = getBoolean("mobs.ravager.controllable", ravagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.ravager.attributes.max-health", ravagerMaxHealth);
|
|
+ set("mobs.ravager.attributes.max-health", null);
|
|
+ set("mobs.ravager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth);
|
|
+ ravagerBypassMobGriefing = getBoolean("mobs.ravager.bypass-mob-griefing", ravagerBypassMobGriefing);
|
|
+ ravagerTakeDamageFromWater = getBoolean("mobs.ravager.takes-damage-from-water", ravagerTakeDamageFromWater);
|
|
+ getList("mobs.ravager.griefable-blocks", new ArrayList<String>(){{
|
|
+ add("minecraft:oak_leaves");
|
|
+ add("minecraft:spruce_leaves");
|
|
+ add("minecraft:birch_leaves");
|
|
+ add("minecraft:jungle_leaves");
|
|
+ add("minecraft:acacia_leaves");
|
|
+ add("minecraft:dark_oak_leaves");
|
|
+ add("minecraft:beetroots");
|
|
+ add("minecraft:carrots");
|
|
+ add("minecraft:potatoes");
|
|
+ add("minecraft:wheat");
|
|
+ }}).forEach(key -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key.toString()));
|
|
+ if (!block.defaultBlockState().isAir()) {
|
|
+ ravagerGriefableBlocks.add(block);
|
|
+ }
|
|
+ });
|
|
+ ravagerAlwaysDropExp = getBoolean("mobs.ravager.always-drop-exp", ravagerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean salmonRidable = false;
|
|
+ public boolean salmonControllable = true;
|
|
+ public double salmonMaxHealth = 3.0D;
|
|
+ public boolean salmonTakeDamageFromWater = false;
|
|
+ public boolean salmonAlwaysDropExp = false;
|
|
+ private void salmonSettings() {
|
|
+ salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable);
|
|
+ salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.salmon.attributes.max-health", salmonMaxHealth);
|
|
+ set("mobs.salmon.attributes.max-health", null);
|
|
+ set("mobs.salmon.attributes.max_health", oldValue);
|
|
+ }
|
|
+ salmonMaxHealth = getDouble("mobs.salmon.attributes.max_health", salmonMaxHealth);
|
|
+ salmonTakeDamageFromWater = getBoolean("mobs.salmon.takes-damage-from-water", salmonTakeDamageFromWater);
|
|
+ salmonAlwaysDropExp = getBoolean("mobs.salmon.always-drop-exp", salmonAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean sheepRidable = false;
|
|
+ public boolean sheepRidableInWater = true;
|
|
+ public boolean sheepControllable = true;
|
|
+ public double sheepMaxHealth = 8.0D;
|
|
+ public int sheepBreedingTicks = 6000;
|
|
+ public boolean sheepBypassMobGriefing = false;
|
|
+ public boolean sheepTakeDamageFromWater = false;
|
|
+ public boolean sheepAlwaysDropExp = false;
|
|
+ public boolean sheepShearJebRandomColor = false;
|
|
+ private void sheepSettings() {
|
|
+ sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable);
|
|
+ sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater);
|
|
+ sheepControllable = getBoolean("mobs.sheep.controllable", sheepControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.sheep.attributes.max-health", sheepMaxHealth);
|
|
+ set("mobs.sheep.attributes.max-health", null);
|
|
+ set("mobs.sheep.attributes.max_health", oldValue);
|
|
+ }
|
|
+ sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth);
|
|
+ sheepBreedingTicks = getInt("mobs.sheep.breeding-delay-ticks", sheepBreedingTicks);
|
|
+ sheepBypassMobGriefing = getBoolean("mobs.sheep.bypass-mob-griefing", sheepBypassMobGriefing);
|
|
+ sheepTakeDamageFromWater = getBoolean("mobs.sheep.takes-damage-from-water", sheepTakeDamageFromWater);
|
|
+ sheepAlwaysDropExp = getBoolean("mobs.sheep.always-drop-exp", sheepAlwaysDropExp);
|
|
+ sheepShearJebRandomColor = getBoolean("mobs.sheep.jeb-shear-random-color", sheepShearJebRandomColor);
|
|
+ }
|
|
+
|
|
+ public boolean shulkerRidable = false;
|
|
+ public boolean shulkerRidableInWater = true;
|
|
+ public boolean shulkerControllable = true;
|
|
+ public double shulkerMaxHealth = 30.0D;
|
|
+ public boolean shulkerTakeDamageFromWater = false;
|
|
+ public float shulkerSpawnFromBulletBaseChance = 1.0F;
|
|
+ public boolean shulkerSpawnFromBulletRequireOpenLid = true;
|
|
+ public double shulkerSpawnFromBulletNearbyRange = 8.0D;
|
|
+ public String shulkerSpawnFromBulletNearbyEquation = "(nearby - 1) / 5.0";
|
|
+ public boolean shulkerSpawnFromBulletRandomColor = false;
|
|
+ public boolean shulkerChangeColorWithDye = false;
|
|
+ public boolean shulkerAlwaysDropExp = false;
|
|
+ private void shulkerSettings() {
|
|
+ shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable);
|
|
+ shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater);
|
|
+ shulkerControllable = getBoolean("mobs.shulker.controllable", shulkerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.shulker.attributes.max-health", shulkerMaxHealth);
|
|
+ set("mobs.shulker.attributes.max-health", null);
|
|
+ set("mobs.shulker.attributes.max_health", oldValue);
|
|
+ }
|
|
+ shulkerMaxHealth = getDouble("mobs.shulker.attributes.max_health", shulkerMaxHealth);
|
|
+ shulkerTakeDamageFromWater = getBoolean("mobs.shulker.takes-damage-from-water", shulkerTakeDamageFromWater);
|
|
+ shulkerSpawnFromBulletBaseChance = (float) getDouble("mobs.shulker.spawn-from-bullet.base-chance", shulkerSpawnFromBulletBaseChance);
|
|
+ shulkerSpawnFromBulletRequireOpenLid = getBoolean("mobs.shulker.spawn-from-bullet.require-open-lid", shulkerSpawnFromBulletRequireOpenLid);
|
|
+ shulkerSpawnFromBulletNearbyRange = getDouble("mobs.shulker.spawn-from-bullet.nearby-range", shulkerSpawnFromBulletNearbyRange);
|
|
+ shulkerSpawnFromBulletNearbyEquation = getString("mobs.shulker.spawn-from-bullet.nearby-equation", shulkerSpawnFromBulletNearbyEquation);
|
|
+ shulkerSpawnFromBulletRandomColor = getBoolean("mobs.shulker.spawn-from-bullet.random-color", shulkerSpawnFromBulletRandomColor);
|
|
+ shulkerChangeColorWithDye = getBoolean("mobs.shulker.change-color-with-dye", shulkerChangeColorWithDye);
|
|
+ shulkerAlwaysDropExp = getBoolean("mobs.shulker.always-drop-exp", shulkerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean silverfishRidable = false;
|
|
+ public boolean silverfishRidableInWater = true;
|
|
+ public boolean silverfishControllable = true;
|
|
+ public double silverfishMaxHealth = 8.0D;
|
|
+ public boolean silverfishBypassMobGriefing = false;
|
|
+ public boolean silverfishTakeDamageFromWater = false;
|
|
+ public boolean silverfishAlwaysDropExp = false;
|
|
+ private void silverfishSettings() {
|
|
+ silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable);
|
|
+ silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater);
|
|
+ silverfishControllable = getBoolean("mobs.silverfish.controllable", silverfishControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.silverfish.attributes.max-health", silverfishMaxHealth);
|
|
+ set("mobs.silverfish.attributes.max-health", null);
|
|
+ set("mobs.silverfish.attributes.max_health", oldValue);
|
|
+ }
|
|
+ silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth);
|
|
+ silverfishBypassMobGriefing = getBoolean("mobs.silverfish.bypass-mob-griefing", silverfishBypassMobGriefing);
|
|
+ silverfishTakeDamageFromWater = getBoolean("mobs.silverfish.takes-damage-from-water", silverfishTakeDamageFromWater);
|
|
+ silverfishAlwaysDropExp = getBoolean("mobs.silverfish.always-drop-exp", silverfishAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean skeletonRidable = false;
|
|
+ public boolean skeletonRidableInWater = true;
|
|
+ public boolean skeletonControllable = true;
|
|
+ public double skeletonMaxHealth = 20.0D;
|
|
+ public boolean skeletonTakeDamageFromWater = false;
|
|
+ public boolean skeletonAlwaysDropExp = false;
|
|
+ public double skeletonHeadVisibilityPercent = 0.5D;
|
|
+ public int skeletonFeedWitherRoses = 0;
|
|
+ public String skeletonBowAccuracy = "14 - difficulty * 4";
|
|
+ public Map<Integer, Float> skeletonBowAccuracyMap = new HashMap<>();
|
|
+ private void skeletonSettings() {
|
|
+ skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable);
|
|
+ skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater);
|
|
+ skeletonControllable = getBoolean("mobs.skeleton.controllable", skeletonControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.skeleton.attributes.max-health", skeletonMaxHealth);
|
|
+ set("mobs.skeleton.attributes.max-health", null);
|
|
+ set("mobs.skeleton.attributes.max_health", oldValue);
|
|
+ }
|
|
+ skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth);
|
|
+ skeletonTakeDamageFromWater = getBoolean("mobs.skeleton.takes-damage-from-water", skeletonTakeDamageFromWater);
|
|
+ skeletonAlwaysDropExp = getBoolean("mobs.skeleton.always-drop-exp", skeletonAlwaysDropExp);
|
|
+ skeletonHeadVisibilityPercent = getDouble("mobs.skeleton.head-visibility-percent", skeletonHeadVisibilityPercent);
|
|
+ skeletonFeedWitherRoses = getInt("mobs.skeleton.feed-wither-roses", skeletonFeedWitherRoses);
|
|
+ final String defaultSkeletonBowAccuracy = skeletonBowAccuracy;
|
|
+ skeletonBowAccuracy = getString("mobs.skeleton.bow-accuracy", skeletonBowAccuracy);
|
|
+ for (int i = 1; i < 4; i++) {
|
|
+ final float divergence;
|
|
+ try {
|
|
+ divergence = ((Number) Entity.scriptEngine.eval("let difficulty = " + i + "; " + skeletonBowAccuracy)).floatValue();
|
|
+ } catch (javax.script.ScriptException e) {
|
|
+ e.printStackTrace();
|
|
+ break;
|
|
+ }
|
|
+ skeletonBowAccuracyMap.put(i, divergence);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean skeletonHorseRidableInWater = true;
|
|
+ public boolean skeletonHorseCanSwim = false;
|
|
+ public double skeletonHorseMaxHealthMin = 15.0D;
|
|
+ public double skeletonHorseMaxHealthMax = 15.0D;
|
|
+ public double skeletonHorseJumpStrengthMin = 0.4D;
|
|
+ public double skeletonHorseJumpStrengthMax = 1.0D;
|
|
+ public double skeletonHorseMovementSpeedMin = 0.2D;
|
|
+ public double skeletonHorseMovementSpeedMax = 0.2D;
|
|
+ public boolean skeletonHorseTakeDamageFromWater = false;
|
|
+ public boolean skeletonHorseAlwaysDropExp = false;
|
|
+ private void skeletonHorseSettings() {
|
|
+ skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater);
|
|
+ skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.skeleton_horse.attributes.max-health", skeletonHorseMaxHealthMin);
|
|
+ set("mobs.skeleton_horse.attributes.max-health", null);
|
|
+ set("mobs.skeleton_horse.attributes.max_health.min", oldValue);
|
|
+ set("mobs.skeleton_horse.attributes.max_health.max", oldValue);
|
|
+ }
|
|
+ skeletonHorseMaxHealthMin = getDouble("mobs.skeleton_horse.attributes.max_health.min", skeletonHorseMaxHealthMin);
|
|
+ skeletonHorseMaxHealthMax = getDouble("mobs.skeleton_horse.attributes.max_health.max", skeletonHorseMaxHealthMax);
|
|
+ skeletonHorseJumpStrengthMin = getDouble("mobs.skeleton_horse.attributes.jump_strength.min", skeletonHorseJumpStrengthMin);
|
|
+ skeletonHorseJumpStrengthMax = getDouble("mobs.skeleton_horse.attributes.jump_strength.max", skeletonHorseJumpStrengthMax);
|
|
+ skeletonHorseMovementSpeedMin = getDouble("mobs.skeleton_horse.attributes.movement_speed.min", skeletonHorseMovementSpeedMin);
|
|
+ skeletonHorseMovementSpeedMax = getDouble("mobs.skeleton_horse.attributes.movement_speed.max", skeletonHorseMovementSpeedMax);
|
|
+ skeletonHorseTakeDamageFromWater = getBoolean("mobs.skeleton_horse.takes-damage-from-water", skeletonHorseTakeDamageFromWater);
|
|
+ skeletonHorseAlwaysDropExp = getBoolean("mobs.skeleton_horse.always-drop-exp", skeletonHorseAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean slimeRidable = false;
|
|
+ public boolean slimeRidableInWater = true;
|
|
+ public boolean slimeControllable = true;
|
|
+ public String slimeMaxHealth = "size * size";
|
|
+ public String slimeAttackDamage = "size";
|
|
+ public Map<Integer, Double> slimeMaxHealthCache = new HashMap<>();
|
|
+ public Map<Integer, Double> slimeAttackDamageCache = new HashMap<>();
|
|
+ public boolean slimeTakeDamageFromWater = false;
|
|
+ public boolean slimeAlwaysDropExp = false;
|
|
+ private void slimeSettings() {
|
|
+ slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable);
|
|
+ slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater);
|
|
+ slimeControllable = getBoolean("mobs.slime.controllable", slimeControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ String oldValue = getString("mobs.slime.attributes.max-health", slimeMaxHealth);
|
|
+ set("mobs.slime.attributes.max-health", null);
|
|
+ set("mobs.slime.attributes.max_health", oldValue);
|
|
+ }
|
|
+ slimeMaxHealth = getString("mobs.slime.attributes.max_health", slimeMaxHealth);
|
|
+ slimeAttackDamage = getString("mobs.slime.attributes.attack_damage", slimeAttackDamage);
|
|
+ slimeMaxHealthCache.clear();
|
|
+ slimeAttackDamageCache.clear();
|
|
+ slimeTakeDamageFromWater = getBoolean("mobs.slime.takes-damage-from-water", slimeTakeDamageFromWater);
|
|
+ slimeAlwaysDropExp = getBoolean("mobs.slime.always-drop-exp", slimeAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean snowGolemRidable = false;
|
|
+ public boolean snowGolemRidableInWater = true;
|
|
+ public boolean snowGolemControllable = true;
|
|
+ public boolean snowGolemLeaveTrailWhenRidden = false;
|
|
+ public double snowGolemMaxHealth = 4.0D;
|
|
+ public boolean snowGolemDropsPumpkin = true;
|
|
+ public boolean snowGolemPutPumpkinBack = false;
|
|
+ public int snowGolemSnowBallMin = 20;
|
|
+ public int snowGolemSnowBallMax = 20;
|
|
+ public float snowGolemSnowBallModifier = 10.0F;
|
|
+ public double snowGolemAttackDistance = 1.25D;
|
|
+ public boolean snowGolemBypassMobGriefing = false;
|
|
+ public boolean snowGolemTakeDamageFromWater = true;
|
|
+ public boolean snowGolemAlwaysDropExp = false;
|
|
+ private void snowGolemSettings() {
|
|
+ snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable);
|
|
+ snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater);
|
|
+ snowGolemControllable = getBoolean("mobs.snow_golem.controllable", snowGolemControllable);
|
|
+ snowGolemLeaveTrailWhenRidden = getBoolean("mobs.snow_golem.leave-trail-when-ridden", snowGolemLeaveTrailWhenRidden);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.snow_golem.attributes.max-health", snowGolemMaxHealth);
|
|
+ set("mobs.snow_golem.attributes.max-health", null);
|
|
+ set("mobs.snow_golem.attributes.max_health", oldValue);
|
|
+ }
|
|
+ snowGolemMaxHealth = getDouble("mobs.snow_golem.attributes.max_health", snowGolemMaxHealth);
|
|
+ snowGolemDropsPumpkin = getBoolean("mobs.snow_golem.drop-pumpkin-when-sheared", snowGolemDropsPumpkin);
|
|
+ snowGolemPutPumpkinBack = getBoolean("mobs.snow_golem.pumpkin-can-be-added-back", snowGolemPutPumpkinBack);
|
|
+ snowGolemSnowBallMin = getInt("mobs.snow_golem.min-shoot-interval-ticks", snowGolemSnowBallMin);
|
|
+ snowGolemSnowBallMax = getInt("mobs.snow_golem.max-shoot-interval-ticks", snowGolemSnowBallMax);
|
|
+ snowGolemSnowBallModifier = (float) getDouble("mobs.snow_golem.snow-ball-modifier", snowGolemSnowBallModifier);
|
|
+ snowGolemAttackDistance = getDouble("mobs.snow_golem.attack-distance", snowGolemAttackDistance);
|
|
+ snowGolemBypassMobGriefing = getBoolean("mobs.snow_golem.bypass-mob-griefing", snowGolemBypassMobGriefing);
|
|
+ snowGolemTakeDamageFromWater = getBoolean("mobs.snow_golem.takes-damage-from-water", snowGolemTakeDamageFromWater);
|
|
+ snowGolemAlwaysDropExp = getBoolean("mobs.snow_golem.always-drop-exp", snowGolemAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean snifferRidable = false;
|
|
+ public boolean snifferRidableInWater = true;
|
|
+ public boolean snifferControllable = true;
|
|
+ public double snifferMaxHealth = 14.0D;
|
|
+ public int snifferBreedingTicks = 6000;
|
|
+ private void snifferSettings() {
|
|
+ snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable);
|
|
+ snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater);
|
|
+ snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable);
|
|
+ snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth);
|
|
+ snifferBreedingTicks = getInt("mobs.sniffer.breeding-delay-ticks", chickenBreedingTicks);
|
|
+ }
|
|
+
|
|
+ public boolean squidRidable = false;
|
|
+ public boolean squidControllable = true;
|
|
+ public double squidMaxHealth = 10.0D;
|
|
+ public boolean squidImmuneToEAR = true;
|
|
+ public double squidOffsetWaterCheck = 0.0D;
|
|
+ public boolean squidsCanFly = false;
|
|
+ public boolean squidTakeDamageFromWater = false;
|
|
+ public boolean squidAlwaysDropExp = false;
|
|
+ private void squidSettings() {
|
|
+ squidRidable = getBoolean("mobs.squid.ridable", squidRidable);
|
|
+ squidControllable = getBoolean("mobs.squid.controllable", squidControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.squid.attributes.max-health", squidMaxHealth);
|
|
+ set("mobs.squid.attributes.max-health", null);
|
|
+ set("mobs.squid.attributes.max_health", oldValue);
|
|
+ }
|
|
+ squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth);
|
|
+ squidImmuneToEAR = getBoolean("mobs.squid.immune-to-EAR", squidImmuneToEAR);
|
|
+ squidOffsetWaterCheck = getDouble("mobs.squid.water-offset-check", squidOffsetWaterCheck);
|
|
+ squidsCanFly = getBoolean("mobs.squid.can-fly", squidsCanFly);
|
|
+ squidTakeDamageFromWater = getBoolean("mobs.squid.takes-damage-from-water", squidTakeDamageFromWater);
|
|
+ squidAlwaysDropExp = getBoolean("mobs.squid.always-drop-exp", squidAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean spiderRidable = false;
|
|
+ public boolean spiderRidableInWater = false;
|
|
+ public boolean spiderControllable = true;
|
|
+ public double spiderMaxHealth = 16.0D;
|
|
+ public boolean spiderTakeDamageFromWater = false;
|
|
+ public boolean spiderAlwaysDropExp = false;
|
|
+ private void spiderSettings() {
|
|
+ spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable);
|
|
+ spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater);
|
|
+ spiderControllable = getBoolean("mobs.spider.controllable", spiderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.spider.attributes.max-health", spiderMaxHealth);
|
|
+ set("mobs.spider.attributes.max-health", null);
|
|
+ set("mobs.spider.attributes.max_health", oldValue);
|
|
+ }
|
|
+ spiderMaxHealth = getDouble("mobs.spider.attributes.max_health", spiderMaxHealth);
|
|
+ spiderTakeDamageFromWater = getBoolean("mobs.spider.takes-damage-from-water", spiderTakeDamageFromWater);
|
|
+ spiderAlwaysDropExp = getBoolean("mobs.spider.always-drop-exp", spiderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean strayRidable = false;
|
|
+ public boolean strayRidableInWater = true;
|
|
+ public boolean strayControllable = true;
|
|
+ public double strayMaxHealth = 20.0D;
|
|
+ public boolean strayTakeDamageFromWater = false;
|
|
+ public boolean strayAlwaysDropExp = false;
|
|
+ private void straySettings() {
|
|
+ strayRidable = getBoolean("mobs.stray.ridable", strayRidable);
|
|
+ strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater);
|
|
+ strayControllable = getBoolean("mobs.stray.controllable", strayControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.stray.attributes.max-health", strayMaxHealth);
|
|
+ set("mobs.stray.attributes.max-health", null);
|
|
+ set("mobs.stray.attributes.max_health", oldValue);
|
|
+ }
|
|
+ strayMaxHealth = getDouble("mobs.stray.attributes.max_health", strayMaxHealth);
|
|
+ strayTakeDamageFromWater = getBoolean("mobs.stray.takes-damage-from-water", strayTakeDamageFromWater);
|
|
+ strayAlwaysDropExp = getBoolean("mobs.stray.always-drop-exp", strayAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean striderRidable = false;
|
|
+ public boolean striderRidableInWater = false;
|
|
+ public boolean striderControllable = true;
|
|
+ public double striderMaxHealth = 20.0D;
|
|
+ public int striderBreedingTicks = 6000;
|
|
+ public boolean striderGiveSaddleBack = false;
|
|
+ public boolean striderTakeDamageFromWater = true;
|
|
+ public boolean striderAlwaysDropExp = false;
|
|
+ private void striderSettings() {
|
|
+ striderRidable = getBoolean("mobs.strider.ridable", striderRidable);
|
|
+ striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater);
|
|
+ striderControllable = getBoolean("mobs.strider.controllable", striderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.strider.attributes.max-health", striderMaxHealth);
|
|
+ set("mobs.strider.attributes.max-health", null);
|
|
+ set("mobs.strider.attributes.max_health", oldValue);
|
|
+ }
|
|
+ striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth);
|
|
+ striderBreedingTicks = getInt("mobs.strider.breeding-delay-ticks", striderBreedingTicks);
|
|
+ striderGiveSaddleBack = getBoolean("mobs.strider.give-saddle-back", striderGiveSaddleBack);
|
|
+ striderTakeDamageFromWater = getBoolean("mobs.strider.takes-damage-from-water", striderTakeDamageFromWater);
|
|
+ striderAlwaysDropExp = getBoolean("mobs.strider.always-drop-exp", striderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean tadpoleRidable = false;
|
|
+ public boolean tadpoleRidableInWater = true;
|
|
+ public boolean tadpoleControllable = true;
|
|
+ private void tadpoleSettings() {
|
|
+ tadpoleRidable = getBoolean("mobs.tadpole.ridable", tadpoleRidable);
|
|
+ tadpoleRidableInWater = getBoolean("mobs.tadpole.ridable-in-water", tadpoleRidableInWater);
|
|
+ tadpoleControllable = getBoolean("mobs.tadpole.controllable", tadpoleControllable);
|
|
+ }
|
|
+
|
|
+ public boolean traderLlamaRidable = false;
|
|
+ public boolean traderLlamaRidableInWater = false;
|
|
+ public boolean traderLlamaControllable = true;
|
|
+ public double traderLlamaMaxHealthMin = 15.0D;
|
|
+ public double traderLlamaMaxHealthMax = 30.0D;
|
|
+ public double traderLlamaJumpStrengthMin = 0.5D;
|
|
+ public double traderLlamaJumpStrengthMax = 0.5D;
|
|
+ public double traderLlamaMovementSpeedMin = 0.175D;
|
|
+ public double traderLlamaMovementSpeedMax = 0.175D;
|
|
+ public int traderLlamaBreedingTicks = 6000;
|
|
+ public boolean traderLlamaTakeDamageFromWater = false;
|
|
+ public boolean traderLlamaAlwaysDropExp = false;
|
|
+ private void traderLlamaSettings() {
|
|
+ traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable);
|
|
+ traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater);
|
|
+ traderLlamaControllable = getBoolean("mobs.trader_llama.controllable", traderLlamaControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldMin = getDouble("mobs.trader_llama.attributes.max-health.min", traderLlamaMaxHealthMin);
|
|
+ double oldMax = getDouble("mobs.trader_llama.attributes.max-health.max", traderLlamaMaxHealthMax);
|
|
+ set("mobs.trader_llama.attributes.max-health", null);
|
|
+ set("mobs.trader_llama.attributes.max_health.min", oldMin);
|
|
+ set("mobs.trader_llama.attributes.max_health.max", oldMax);
|
|
+ }
|
|
+ traderLlamaMaxHealthMin = getDouble("mobs.trader_llama.attributes.max_health.min", traderLlamaMaxHealthMin);
|
|
+ traderLlamaMaxHealthMax = getDouble("mobs.trader_llama.attributes.max_health.max", traderLlamaMaxHealthMax);
|
|
+ traderLlamaJumpStrengthMin = getDouble("mobs.trader_llama.attributes.jump_strength.min", traderLlamaJumpStrengthMin);
|
|
+ traderLlamaJumpStrengthMax = getDouble("mobs.trader_llama.attributes.jump_strength.max", traderLlamaJumpStrengthMax);
|
|
+ traderLlamaMovementSpeedMin = getDouble("mobs.trader_llama.attributes.movement_speed.min", traderLlamaMovementSpeedMin);
|
|
+ traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax);
|
|
+ traderLlamaBreedingTicks = getInt("mobs.trader_llama.breeding-delay-ticks", traderLlamaBreedingTicks);
|
|
+ traderLlamaTakeDamageFromWater = getBoolean("mobs.trader_llama.takes-damage-from-water", traderLlamaTakeDamageFromWater);
|
|
+ traderLlamaAlwaysDropExp = getBoolean("mobs.trader_llama.always-drop-exp", traderLlamaAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean tropicalFishRidable = false;
|
|
+ public boolean tropicalFishControllable = true;
|
|
+ public double tropicalFishMaxHealth = 3.0D;
|
|
+ public boolean tropicalFishTakeDamageFromWater = false;
|
|
+ public boolean tropicalFishAlwaysDropExp = false;
|
|
+ private void tropicalFishSettings() {
|
|
+ tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable);
|
|
+ tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.tropical_fish.attributes.max-health", tropicalFishMaxHealth);
|
|
+ set("mobs.tropical_fish.attributes.max-health", null);
|
|
+ set("mobs.tropical_fish.attributes.max_health", oldValue);
|
|
+ }
|
|
+ tropicalFishMaxHealth = getDouble("mobs.tropical_fish.attributes.max_health", tropicalFishMaxHealth);
|
|
+ tropicalFishTakeDamageFromWater = getBoolean("mobs.tropical_fish.takes-damage-from-water", tropicalFishTakeDamageFromWater);
|
|
+ tropicalFishAlwaysDropExp = getBoolean("mobs.tropical_fish.always-drop-exp", tropicalFishAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean turtleRidable = false;
|
|
+ public boolean turtleRidableInWater = true;
|
|
+ public boolean turtleControllable = true;
|
|
+ public double turtleMaxHealth = 30.0D;
|
|
+ public int turtleBreedingTicks = 6000;
|
|
+ public boolean turtleTakeDamageFromWater = false;
|
|
+ public boolean turtleAlwaysDropExp = false;
|
|
+ private void turtleSettings() {
|
|
+ turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable);
|
|
+ turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater);
|
|
+ turtleControllable = getBoolean("mobs.turtle.controllable", turtleControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.turtle.attributes.max-health", turtleMaxHealth);
|
|
+ set("mobs.turtle.attributes.max-health", null);
|
|
+ set("mobs.turtle.attributes.max_health", oldValue);
|
|
+ }
|
|
+ turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth);
|
|
+ turtleBreedingTicks = getInt("mobs.turtle.breeding-delay-ticks", turtleBreedingTicks);
|
|
+ turtleTakeDamageFromWater = getBoolean("mobs.turtle.takes-damage-from-water", turtleTakeDamageFromWater);
|
|
+ turtleAlwaysDropExp = getBoolean("mobs.turtle.always-drop-exp", turtleAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean vexRidable = false;
|
|
+ public boolean vexRidableInWater = true;
|
|
+ public boolean vexControllable = true;
|
|
+ public double vexMaxY = 320D;
|
|
+ public double vexMaxHealth = 14.0D;
|
|
+ public boolean vexTakeDamageFromWater = false;
|
|
+ public boolean vexAlwaysDropExp = false;
|
|
+ private void vexSettings() {
|
|
+ vexRidable = getBoolean("mobs.vex.ridable", vexRidable);
|
|
+ vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater);
|
|
+ vexControllable = getBoolean("mobs.vex.controllable", vexControllable);
|
|
+ vexMaxY = getDouble("mobs.vex.ridable-max-y", vexMaxY);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.vex.attributes.max-health", vexMaxHealth);
|
|
+ set("mobs.vex.attributes.max-health", null);
|
|
+ set("mobs.vex.attributes.max_health", oldValue);
|
|
+ }
|
|
+ vexMaxHealth = getDouble("mobs.vex.attributes.max_health", vexMaxHealth);
|
|
+ vexTakeDamageFromWater = getBoolean("mobs.vex.takes-damage-from-water", vexTakeDamageFromWater);
|
|
+ vexAlwaysDropExp = getBoolean("mobs.vex.always-drop-exp", vexAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean villagerRidable = false;
|
|
+ public boolean villagerRidableInWater = true;
|
|
+ public boolean villagerControllable = true;
|
|
+ public double villagerMaxHealth = 20.0D;
|
|
+ public boolean villagerFollowEmeraldBlock = false;
|
|
+ public boolean villagerCanBeLeashed = false;
|
|
+ public boolean villagerCanBreed = true;
|
|
+ public int villagerBreedingTicks = 6000;
|
|
+ public boolean villagerClericsFarmWarts = false;
|
|
+ public boolean villagerClericFarmersThrowWarts = true;
|
|
+ public boolean villagerBypassMobGriefing = false;
|
|
+ public boolean villagerTakeDamageFromWater = false;
|
|
+ public boolean villagerAllowTrading = true;
|
|
+ public boolean villagerAlwaysDropExp = false;
|
|
+ public int villagerMinimumDemand = 0;
|
|
+ public boolean villagerLobotomizeEnabled = false;
|
|
+ public int villagerLobotomizeCheckInterval = 100;
|
|
+ public boolean villagerDisplayTradeItem = true;
|
|
+ public int villagerSpawnIronGolemRadius = 0;
|
|
+ public int villagerSpawnIronGolemLimit = 0;
|
|
+ private void villagerSettings() {
|
|
+ villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable);
|
|
+ villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater);
|
|
+ villagerControllable = getBoolean("mobs.villager.controllable", villagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.villager.attributes.max-health", villagerMaxHealth);
|
|
+ set("mobs.villager.attributes.max-health", null);
|
|
+ set("mobs.villager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth);
|
|
+ villagerFollowEmeraldBlock = getBoolean("mobs.villager.follow-emerald-blocks", villagerFollowEmeraldBlock);
|
|
+ villagerCanBeLeashed = getBoolean("mobs.villager.can-be-leashed", villagerCanBeLeashed);
|
|
+ villagerCanBreed = getBoolean("mobs.villager.can-breed", villagerCanBreed);
|
|
+ villagerBreedingTicks = getInt("mobs.villager.breeding-delay-ticks", villagerBreedingTicks);
|
|
+ villagerClericsFarmWarts = getBoolean("mobs.villager.clerics-farm-warts", villagerClericsFarmWarts);
|
|
+ villagerClericFarmersThrowWarts = getBoolean("mobs.villager.cleric-wart-farmers-throw-warts-at-villagers", villagerClericFarmersThrowWarts);
|
|
+ villagerBypassMobGriefing = getBoolean("mobs.villager.bypass-mob-griefing", villagerBypassMobGriefing);
|
|
+ villagerTakeDamageFromWater = getBoolean("mobs.villager.takes-damage-from-water", villagerTakeDamageFromWater);
|
|
+ villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading);
|
|
+ villagerAlwaysDropExp = getBoolean("mobs.villager.always-drop-exp", villagerAlwaysDropExp);
|
|
+ villagerMinimumDemand = getInt("mobs.villager.minimum-demand", villagerMinimumDemand);
|
|
+ if (PurpurConfig.version < 9) {
|
|
+ boolean oldValue = getBoolean("mobs.villager.lobotomize-1x1", villagerLobotomizeEnabled);
|
|
+ set("mobs.villager.lobotomize.enabled", oldValue);
|
|
+ set("mobs.villager.lobotomize-1x1", null);
|
|
+ }
|
|
+ if (PurpurConfig.version < 27) {
|
|
+ int oldValue = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval);
|
|
+ set("mobs.villager.lobotomize.check-interval", oldValue == 60 ? 100 : oldValue);
|
|
+ }
|
|
+ villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled);
|
|
+ villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval);
|
|
+ villagerDisplayTradeItem = getBoolean("mobs.villager.display-trade-item", villagerDisplayTradeItem);
|
|
+ villagerSpawnIronGolemRadius = getInt("mobs.villager.spawn-iron-golem.radius", villagerSpawnIronGolemRadius);
|
|
+ villagerSpawnIronGolemLimit = getInt("mobs.villager.spawn-iron-golem.limit", villagerSpawnIronGolemLimit);
|
|
+ }
|
|
+
|
|
+ public boolean vindicatorRidable = false;
|
|
+ public boolean vindicatorRidableInWater = true;
|
|
+ public boolean vindicatorControllable = true;
|
|
+ public double vindicatorMaxHealth = 24.0D;
|
|
+ public double vindicatorJohnnySpawnChance = 0D;
|
|
+ public boolean vindicatorTakeDamageFromWater = false;
|
|
+ public boolean vindicatorAlwaysDropExp = false;
|
|
+ private void vindicatorSettings() {
|
|
+ vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable);
|
|
+ vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater);
|
|
+ vindicatorControllable = getBoolean("mobs.vindicator.controllable", vindicatorControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.vindicator.attributes.max-health", vindicatorMaxHealth);
|
|
+ set("mobs.vindicator.attributes.max-health", null);
|
|
+ set("mobs.vindicator.attributes.max_health", oldValue);
|
|
+ }
|
|
+ vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth);
|
|
+ vindicatorJohnnySpawnChance = getDouble("mobs.vindicator.johnny.spawn-chance", vindicatorJohnnySpawnChance);
|
|
+ vindicatorTakeDamageFromWater = getBoolean("mobs.vindicator.takes-damage-from-water", vindicatorTakeDamageFromWater);
|
|
+ vindicatorAlwaysDropExp = getBoolean("mobs.vindicator.always-drop-exp", vindicatorAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean wanderingTraderRidable = false;
|
|
+ public boolean wanderingTraderRidableInWater = true;
|
|
+ public boolean wanderingTraderControllable = true;
|
|
+ public double wanderingTraderMaxHealth = 20.0D;
|
|
+ public boolean wanderingTraderFollowEmeraldBlock = false;
|
|
+ public boolean wanderingTraderCanBeLeashed = false;
|
|
+ public boolean wanderingTraderTakeDamageFromWater = false;
|
|
+ public boolean wanderingTraderAllowTrading = true;
|
|
+ public boolean wanderingTraderAlwaysDropExp = false;
|
|
+ private void wanderingTraderSettings() {
|
|
+ wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable);
|
|
+ wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater);
|
|
+ wanderingTraderControllable = getBoolean("mobs.wandering_trader.controllable", wanderingTraderControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wandering_trader.attributes.max-health", wanderingTraderMaxHealth);
|
|
+ set("mobs.wandering_trader.attributes.max-health", null);
|
|
+ set("mobs.wandering_trader.attributes.max_health", oldValue);
|
|
+ }
|
|
+ wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth);
|
|
+ wanderingTraderFollowEmeraldBlock = getBoolean("mobs.wandering_trader.follow-emerald-blocks", wanderingTraderFollowEmeraldBlock);
|
|
+ wanderingTraderCanBeLeashed = getBoolean("mobs.wandering_trader.can-be-leashed", wanderingTraderCanBeLeashed);
|
|
+ wanderingTraderTakeDamageFromWater = getBoolean("mobs.wandering_trader.takes-damage-from-water", wanderingTraderTakeDamageFromWater);
|
|
+ wanderingTraderAllowTrading = getBoolean("mobs.wandering_trader.allow-trading", wanderingTraderAllowTrading);
|
|
+ wanderingTraderAlwaysDropExp = getBoolean("mobs.wandering_trader.always-drop-exp", wanderingTraderAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean wardenRidable = false;
|
|
+ public boolean wardenRidableInWater = true;
|
|
+ public boolean wardenControllable = true;
|
|
+ private void wardenSettings() {
|
|
+ wardenRidable = getBoolean("mobs.warden.ridable", wardenRidable);
|
|
+ wardenRidableInWater = getBoolean("mobs.warden.ridable-in-water", wardenRidableInWater);
|
|
+ wardenControllable = getBoolean("mobs.warden.controllable", wardenControllable);
|
|
+ }
|
|
+
|
|
+ public boolean witchRidable = false;
|
|
+ public boolean witchRidableInWater = true;
|
|
+ public boolean witchControllable = true;
|
|
+ public double witchMaxHealth = 26.0D;
|
|
+ public boolean witchTakeDamageFromWater = false;
|
|
+ public boolean witchAlwaysDropExp = false;
|
|
+ private void witchSettings() {
|
|
+ witchRidable = getBoolean("mobs.witch.ridable", witchRidable);
|
|
+ witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater);
|
|
+ witchControllable = getBoolean("mobs.witch.controllable", witchControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.witch.attributes.max-health", witchMaxHealth);
|
|
+ set("mobs.witch.attributes.max-health", null);
|
|
+ set("mobs.witch.attributes.max_health", oldValue);
|
|
+ }
|
|
+ witchMaxHealth = getDouble("mobs.witch.attributes.max_health", witchMaxHealth);
|
|
+ witchTakeDamageFromWater = getBoolean("mobs.witch.takes-damage-from-water", witchTakeDamageFromWater);
|
|
+ witchAlwaysDropExp = getBoolean("mobs.witch.always-drop-exp", witchAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean witherRidable = false;
|
|
+ public boolean witherRidableInWater = true;
|
|
+ public boolean witherControllable = true;
|
|
+ public double witherMaxY = 320D;
|
|
+ public double witherMaxHealth = 300.0D;
|
|
+ public float witherHealthRegenAmount = 1.0f;
|
|
+ public int witherHealthRegenDelay = 20;
|
|
+ public boolean witherBypassMobGriefing = false;
|
|
+ public boolean witherTakeDamageFromWater = false;
|
|
+ public boolean witherCanRideVehicles = false;
|
|
+ public float witherExplosionRadius = 1.0F;
|
|
+ public boolean witherPlaySpawnSound = true;
|
|
+ public boolean witherAlwaysDropExp = false;
|
|
+ private void witherSettings() {
|
|
+ witherRidable = getBoolean("mobs.wither.ridable", witherRidable);
|
|
+ witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater);
|
|
+ witherControllable = getBoolean("mobs.wither.controllable", witherControllable);
|
|
+ witherMaxY = getDouble("mobs.wither.ridable-max-y", witherMaxY);
|
|
+ if (PurpurConfig.version < 8) {
|
|
+ double oldValue = getDouble("mobs.wither.max-health", witherMaxHealth);
|
|
+ set("mobs.wither.max_health", null);
|
|
+ set("mobs.wither.attributes.max-health", oldValue);
|
|
+ } else if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wither.attributes.max-health", witherMaxHealth);
|
|
+ set("mobs.wither.attributes.max-health", null);
|
|
+ set("mobs.wither.attributes.max_health", oldValue);
|
|
+ }
|
|
+ witherMaxHealth = getDouble("mobs.wither.attributes.max_health", witherMaxHealth);
|
|
+ witherHealthRegenAmount = (float) getDouble("mobs.wither.health-regen-amount", witherHealthRegenAmount);
|
|
+ witherHealthRegenDelay = getInt("mobs.wither.health-regen-delay", witherHealthRegenDelay);
|
|
+ witherBypassMobGriefing = getBoolean("mobs.wither.bypass-mob-griefing", witherBypassMobGriefing);
|
|
+ witherTakeDamageFromWater = getBoolean("mobs.wither.takes-damage-from-water", witherTakeDamageFromWater);
|
|
+ witherCanRideVehicles = getBoolean("mobs.wither.can-ride-vehicles", witherCanRideVehicles);
|
|
+ witherExplosionRadius = (float) getDouble("mobs.wither.explosion-radius", witherExplosionRadius);
|
|
+ witherPlaySpawnSound = getBoolean("mobs.wither.play-spawn-sound", witherPlaySpawnSound);
|
|
+ witherAlwaysDropExp = getBoolean("mobs.wither.always-drop-exp", witherAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean witherSkeletonRidable = false;
|
|
+ public boolean witherSkeletonRidableInWater = true;
|
|
+ public boolean witherSkeletonControllable = true;
|
|
+ public double witherSkeletonMaxHealth = 20.0D;
|
|
+ public boolean witherSkeletonTakeDamageFromWater = false;
|
|
+ public boolean witherSkeletonAlwaysDropExp = false;
|
|
+ private void witherSkeletonSettings() {
|
|
+ witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable);
|
|
+ witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater);
|
|
+ witherSkeletonControllable = getBoolean("mobs.wither_skeleton.controllable", witherSkeletonControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wither_skeleton.attributes.max-health", witherSkeletonMaxHealth);
|
|
+ set("mobs.wither_skeleton.attributes.max-health", null);
|
|
+ set("mobs.wither_skeleton.attributes.max_health", oldValue);
|
|
+ }
|
|
+ witherSkeletonMaxHealth = getDouble("mobs.wither_skeleton.attributes.max_health", witherSkeletonMaxHealth);
|
|
+ witherSkeletonTakeDamageFromWater = getBoolean("mobs.wither_skeleton.takes-damage-from-water", witherSkeletonTakeDamageFromWater);
|
|
+ witherSkeletonAlwaysDropExp = getBoolean("mobs.wither_skeleton.always-drop-exp", witherSkeletonAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean wolfRidable = false;
|
|
+ public boolean wolfRidableInWater = true;
|
|
+ public boolean wolfControllable = true;
|
|
+ public double wolfMaxHealth = 8.0D;
|
|
+ public DyeColor wolfDefaultCollarColor = DyeColor.RED;
|
|
+ public boolean wolfMilkCuresRabies = true;
|
|
+ public double wolfNaturalRabid = 0.0D;
|
|
+ public int wolfBreedingTicks = 6000;
|
|
+ public boolean wolfTakeDamageFromWater = false;
|
|
+ public boolean wolfAlwaysDropExp = false;
|
|
+ private void wolfSettings() {
|
|
+ wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable);
|
|
+ wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater);
|
|
+ wolfControllable = getBoolean("mobs.wolf.controllable", wolfControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.wolf.attributes.max-health", wolfMaxHealth);
|
|
+ set("mobs.wolf.attributes.max-health", null);
|
|
+ set("mobs.wolf.attributes.max_health", oldValue);
|
|
+ }
|
|
+ wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth);
|
|
+ try {
|
|
+ wolfDefaultCollarColor = DyeColor.valueOf(getString("mobs.wolf.default-collar-color", wolfDefaultCollarColor.name()));
|
|
+ } catch (IllegalArgumentException ignore) {
|
|
+ wolfDefaultCollarColor = DyeColor.RED;
|
|
+ }
|
|
+ wolfMilkCuresRabies = getBoolean("mobs.wolf.milk-cures-rabid-wolves", wolfMilkCuresRabies);
|
|
+ wolfNaturalRabid = getDouble("mobs.wolf.spawn-rabid-chance", wolfNaturalRabid);
|
|
+ wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks);
|
|
+ wolfTakeDamageFromWater = getBoolean("mobs.wolf.takes-damage-from-water", wolfTakeDamageFromWater);
|
|
+ wolfAlwaysDropExp = getBoolean("mobs.wolf.always-drop-exp", wolfAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zoglinRidable = false;
|
|
+ public boolean zoglinRidableInWater = true;
|
|
+ public boolean zoglinControllable = true;
|
|
+ public double zoglinMaxHealth = 40.0D;
|
|
+ public boolean zoglinTakeDamageFromWater = false;
|
|
+ public boolean zoglinAlwaysDropExp = false;
|
|
+ private void zoglinSettings() {
|
|
+ zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable);
|
|
+ zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater);
|
|
+ zoglinControllable = getBoolean("mobs.zoglin.controllable", zoglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zoglin.attributes.max-health", zoglinMaxHealth);
|
|
+ set("mobs.zoglin.attributes.max-health", null);
|
|
+ set("mobs.zoglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zoglinMaxHealth = getDouble("mobs.zoglin.attributes.max_health", zoglinMaxHealth);
|
|
+ zoglinTakeDamageFromWater = getBoolean("mobs.zoglin.takes-damage-from-water", zoglinTakeDamageFromWater);
|
|
+ zoglinAlwaysDropExp = getBoolean("mobs.zoglin.always-drop-exp", zoglinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zombieRidable = false;
|
|
+ public boolean zombieRidableInWater = true;
|
|
+ public boolean zombieControllable = true;
|
|
+ public double zombieMaxHealth = 20.0D;
|
|
+ public double zombieSpawnReinforcements = 0.1D;
|
|
+ public boolean zombieJockeyOnlyBaby = true;
|
|
+ public double zombieJockeyChance = 0.05D;
|
|
+ public boolean zombieJockeyTryExistingChickens = true;
|
|
+ public boolean zombieAggressiveTowardsVillagerWhenLagging = true;
|
|
+ public boolean zombieBypassMobGriefing = false;
|
|
+ public boolean zombieTakeDamageFromWater = false;
|
|
+ public boolean zombieAlwaysDropExp = false;
|
|
+ public double zombieHeadVisibilityPercent = 0.5D;
|
|
+ private void zombieSettings() {
|
|
+ zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable);
|
|
+ zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater);
|
|
+ zombieControllable = getBoolean("mobs.zombie.controllable", zombieControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombie.attributes.max-health", zombieMaxHealth);
|
|
+ set("mobs.zombie.attributes.max-health", null);
|
|
+ set("mobs.zombie.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zombieMaxHealth = getDouble("mobs.zombie.attributes.max_health", zombieMaxHealth);
|
|
+ zombieSpawnReinforcements = getDouble("mobs.zombie.attributes.spawn_reinforcements", zombieSpawnReinforcements);
|
|
+ zombieJockeyOnlyBaby = getBoolean("mobs.zombie.jockey.only-babies", zombieJockeyOnlyBaby);
|
|
+ zombieJockeyChance = getDouble("mobs.zombie.jockey.chance", zombieJockeyChance);
|
|
+ zombieJockeyTryExistingChickens = getBoolean("mobs.zombie.jockey.try-existing-chickens", zombieJockeyTryExistingChickens);
|
|
+ zombieAggressiveTowardsVillagerWhenLagging = getBoolean("mobs.zombie.aggressive-towards-villager-when-lagging", zombieAggressiveTowardsVillagerWhenLagging);
|
|
+ zombieBypassMobGriefing = getBoolean("mobs.zombie.bypass-mob-griefing", zombieBypassMobGriefing);
|
|
+ zombieTakeDamageFromWater = getBoolean("mobs.zombie.takes-damage-from-water", zombieTakeDamageFromWater);
|
|
+ zombieAlwaysDropExp = getBoolean("mobs.zombie.always-drop-exp", zombieAlwaysDropExp);
|
|
+ zombieHeadVisibilityPercent = getDouble("mobs.zombie.head-visibility-percent", zombieHeadVisibilityPercent);
|
|
+ }
|
|
+
|
|
+ public boolean zombieHorseRidableInWater = false;
|
|
+ public boolean zombieHorseCanSwim = false;
|
|
+ public double zombieHorseMaxHealthMin = 15.0D;
|
|
+ public double zombieHorseMaxHealthMax = 15.0D;
|
|
+ public double zombieHorseJumpStrengthMin = 0.4D;
|
|
+ public double zombieHorseJumpStrengthMax = 1.0D;
|
|
+ public double zombieHorseMovementSpeedMin = 0.2D;
|
|
+ public double zombieHorseMovementSpeedMax = 0.2D;
|
|
+ public double zombieHorseSpawnChance = 0.0D;
|
|
+ public boolean zombieHorseTakeDamageFromWater = false;
|
|
+ public boolean zombieHorseAlwaysDropExp = false;
|
|
+ private void zombieHorseSettings() {
|
|
+ zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater);
|
|
+ zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombie_horse.attributes.max-health", zombieHorseMaxHealthMin);
|
|
+ set("mobs.zombie_horse.attributes.max-health", null);
|
|
+ set("mobs.zombie_horse.attributes.max_health.min", oldValue);
|
|
+ set("mobs.zombie_horse.attributes.max_health.max", oldValue);
|
|
+ }
|
|
+ zombieHorseMaxHealthMin = getDouble("mobs.zombie_horse.attributes.max_health.min", zombieHorseMaxHealthMin);
|
|
+ zombieHorseMaxHealthMax = getDouble("mobs.zombie_horse.attributes.max_health.max", zombieHorseMaxHealthMax);
|
|
+ zombieHorseJumpStrengthMin = getDouble("mobs.zombie_horse.attributes.jump_strength.min", zombieHorseJumpStrengthMin);
|
|
+ zombieHorseJumpStrengthMax = getDouble("mobs.zombie_horse.attributes.jump_strength.max", zombieHorseJumpStrengthMax);
|
|
+ zombieHorseMovementSpeedMin = getDouble("mobs.zombie_horse.attributes.movement_speed.min", zombieHorseMovementSpeedMin);
|
|
+ zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax);
|
|
+ zombieHorseSpawnChance = getDouble("mobs.zombie_horse.spawn-chance", zombieHorseSpawnChance);
|
|
+ zombieHorseTakeDamageFromWater = getBoolean("mobs.zombie_horse.takes-damage-from-water", zombieHorseTakeDamageFromWater);
|
|
+ zombieHorseAlwaysDropExp = getBoolean("mobs.zombie_horse.always-drop-exp", zombieHorseAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zombieVillagerRidable = false;
|
|
+ public boolean zombieVillagerRidableInWater = true;
|
|
+ public boolean zombieVillagerControllable = true;
|
|
+ public double zombieVillagerMaxHealth = 20.0D;
|
|
+ public double zombieVillagerSpawnReinforcements = 0.1D;
|
|
+ public boolean zombieVillagerJockeyOnlyBaby = true;
|
|
+ public double zombieVillagerJockeyChance = 0.05D;
|
|
+ public boolean zombieVillagerJockeyTryExistingChickens = true;
|
|
+ public boolean zombieVillagerTakeDamageFromWater = false;
|
|
+ public int zombieVillagerCuringTimeMin = 3600;
|
|
+ public int zombieVillagerCuringTimeMax = 6000;
|
|
+ public boolean zombieVillagerCureEnabled = true;
|
|
+ public boolean zombieVillagerAlwaysDropExp = false;
|
|
+ private void zombieVillagerSettings() {
|
|
+ zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable);
|
|
+ zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater);
|
|
+ zombieVillagerControllable = getBoolean("mobs.zombie_villager.controllable", zombieVillagerControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombie_villager.attributes.max-health", zombieVillagerMaxHealth);
|
|
+ set("mobs.zombie_villager.attributes.max-health", null);
|
|
+ set("mobs.zombie_villager.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zombieVillagerMaxHealth = getDouble("mobs.zombie_villager.attributes.max_health", zombieVillagerMaxHealth);
|
|
+ zombieVillagerSpawnReinforcements = getDouble("mobs.zombie_villager.attributes.spawn_reinforcements", zombieVillagerSpawnReinforcements);
|
|
+ zombieVillagerJockeyOnlyBaby = getBoolean("mobs.zombie_villager.jockey.only-babies", zombieVillagerJockeyOnlyBaby);
|
|
+ zombieVillagerJockeyChance = getDouble("mobs.zombie_villager.jockey.chance", zombieVillagerJockeyChance);
|
|
+ zombieVillagerJockeyTryExistingChickens = getBoolean("mobs.zombie_villager.jockey.try-existing-chickens", zombieVillagerJockeyTryExistingChickens);
|
|
+ zombieVillagerTakeDamageFromWater = getBoolean("mobs.zombie_villager.takes-damage-from-water", zombieVillagerTakeDamageFromWater);
|
|
+ zombieVillagerCuringTimeMin = getInt("mobs.zombie_villager.curing_time.min", zombieVillagerCuringTimeMin);
|
|
+ zombieVillagerCuringTimeMax = getInt("mobs.zombie_villager.curing_time.max", zombieVillagerCuringTimeMax);
|
|
+ zombieVillagerCureEnabled = getBoolean("mobs.zombie_villager.cure.enabled", zombieVillagerCureEnabled);
|
|
+ zombieVillagerAlwaysDropExp = getBoolean("mobs.zombie_villager.always-drop-exp", zombieVillagerAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public boolean zombifiedPiglinRidable = false;
|
|
+ public boolean zombifiedPiglinRidableInWater = true;
|
|
+ public boolean zombifiedPiglinControllable = true;
|
|
+ public double zombifiedPiglinMaxHealth = 20.0D;
|
|
+ public double zombifiedPiglinSpawnReinforcements = 0.0D;
|
|
+ public boolean zombifiedPiglinJockeyOnlyBaby = true;
|
|
+ public double zombifiedPiglinJockeyChance = 0.05D;
|
|
+ public boolean zombifiedPiglinJockeyTryExistingChickens = true;
|
|
+ public boolean zombifiedPiglinCountAsPlayerKillWhenAngry = true;
|
|
+ public boolean zombifiedPiglinTakeDamageFromWater = false;
|
|
+ public boolean zombifiedPiglinAlwaysDropExp = false;
|
|
+ private void zombifiedPiglinSettings() {
|
|
+ zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable);
|
|
+ zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater);
|
|
+ zombifiedPiglinControllable = getBoolean("mobs.zombified_piglin.controllable", zombifiedPiglinControllable);
|
|
+ if (PurpurConfig.version < 10) {
|
|
+ double oldValue = getDouble("mobs.zombified_piglin.attributes.max-health", zombifiedPiglinMaxHealth);
|
|
+ set("mobs.zombified_piglin.attributes.max-health", null);
|
|
+ set("mobs.zombified_piglin.attributes.max_health", oldValue);
|
|
+ }
|
|
+ zombifiedPiglinMaxHealth = getDouble("mobs.zombified_piglin.attributes.max_health", zombifiedPiglinMaxHealth);
|
|
+ zombifiedPiglinSpawnReinforcements = getDouble("mobs.zombified_piglin.attributes.spawn_reinforcements", zombifiedPiglinSpawnReinforcements);
|
|
+ zombifiedPiglinJockeyOnlyBaby = getBoolean("mobs.zombified_piglin.jockey.only-babies", zombifiedPiglinJockeyOnlyBaby);
|
|
+ zombifiedPiglinJockeyChance = getDouble("mobs.zombified_piglin.jockey.chance", zombifiedPiglinJockeyChance);
|
|
+ zombifiedPiglinJockeyTryExistingChickens = getBoolean("mobs.zombified_piglin.jockey.try-existing-chickens", zombifiedPiglinJockeyTryExistingChickens);
|
|
+ zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry);
|
|
+ zombifiedPiglinTakeDamageFromWater = getBoolean("mobs.zombified_piglin.takes-damage-from-water", zombifiedPiglinTakeDamageFromWater);
|
|
+ zombifiedPiglinAlwaysDropExp = getBoolean("mobs.zombified_piglin.always-drop-exp", zombifiedPiglinAlwaysDropExp);
|
|
+ }
|
|
+
|
|
+ public float hungerStarvationDamage = 1.0F;
|
|
+ private void hungerSettings() {
|
|
+ hungerStarvationDamage = (float) getDouble("hunger.starvation-damage", hungerStarvationDamage);
|
|
+ }
|
|
+
|
|
+ public int conduitDistance = 16;
|
|
+ public double conduitDamageDistance = 8;
|
|
+ public float conduitDamageAmount = 4;
|
|
+ public Block[] conduitBlocks;
|
|
+ private void conduitSettings() {
|
|
+ conduitDistance = getInt("blocks.conduit.effect-distance", conduitDistance);
|
|
+ conduitDamageDistance = getDouble("blocks.conduit.mob-damage.distance", conduitDamageDistance);
|
|
+ conduitDamageAmount = (float) getDouble("blocks.conduit.mob-damage.damage-amount", conduitDamageAmount);
|
|
+ List<Block> conduitBlockList = new ArrayList<>();
|
|
+ getList("blocks.conduit.valid-ring-blocks", new ArrayList<String>(){{
|
|
+ add("minecraft:prismarine");
|
|
+ add("minecraft:prismarine_bricks");
|
|
+ add("minecraft:sea_lantern");
|
|
+ add("minecraft:dark_prismarine");
|
|
+ }}).forEach(key -> {
|
|
+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key.toString()));
|
|
+ if (!block.defaultBlockState().isAir()) {
|
|
+ conduitBlockList.add(block);
|
|
+ }
|
|
+ });
|
|
+ conduitBlocks = conduitBlockList.toArray(Block[]::new);
|
|
+ }
|
|
+
|
|
+ public float cauldronRainChance = 0.05F;
|
|
+ public float cauldronPowderSnowChance = 0.1F;
|
|
+ public float cauldronDripstoneWaterFillChance = 0.17578125F;
|
|
+ public float cauldronDripstoneLavaFillChance = 0.05859375F;
|
|
+ private void cauldronSettings() {
|
|
+ cauldronRainChance = (float) getDouble("blocks.cauldron.fill-chances.rain", cauldronRainChance);
|
|
+ cauldronPowderSnowChance = (float) getDouble("blocks.cauldron.fill-chances.powder-snow", cauldronPowderSnowChance);
|
|
+ cauldronDripstoneWaterFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-water", cauldronDripstoneWaterFillChance);
|
|
+ cauldronDripstoneLavaFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-lava", cauldronDripstoneLavaFillChance);
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/CompassCommand.java b/src/main/java/org/purpurmc/purpur/command/CompassCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..79b8490832d2a0cc7846ddcb091cb6bcac74ea45
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/CompassCommand.java
|
|
@@ -0,0 +1,27 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.task.CompassTask;
|
|
+
|
|
+public class CompassCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("compass")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.compass"))
|
|
+ .executes(context -> {
|
|
+ ServerPlayer player = context.getSource().getPlayerOrException();
|
|
+ CompassTask task = CompassTask.instance();
|
|
+ if (player.compassBar()) {
|
|
+ task.removePlayer(player.getBukkitEntity());
|
|
+ player.compassBar(false);
|
|
+ } else {
|
|
+ task.addPlayer(player.getBukkitEntity());
|
|
+ player.compassBar(true);
|
|
+ }
|
|
+ return 1;
|
|
+ })
|
|
+ );
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java b/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..40d2fab4a9728ac90c36e30c130f3116b7025d11
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java
|
|
@@ -0,0 +1,35 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class CreditsCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("credits")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.credits"))
|
|
+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.credits.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.WIN_GAME, 1F);
|
|
+ player.connection.send(packet);
|
|
+ String output = String.format(PurpurConfig.creditsCommandOutput, player.getGameProfile().getName());
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/DemoCommand.java b/src/main/java/org/purpurmc/purpur/command/DemoCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..235f3cd89f675b70a6152a00534608c0902f19fd
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/DemoCommand.java
|
|
@@ -0,0 +1,35 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class DemoCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("demo")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.demo"))
|
|
+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.demo.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.DEMO_EVENT, 0);
|
|
+ player.connection.send(packet);
|
|
+ String output = String.format(PurpurConfig.demoCommandOutput, player.getGameProfile().getName());
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/PingCommand.java b/src/main/java/org/purpurmc/purpur/command/PingCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..179727c6b3171c040d1aaf069525f61a9a2d54d9
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/PingCommand.java
|
|
@@ -0,0 +1,33 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.bukkit.craftbukkit.util.CraftChatMessage;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class PingCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("ping")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.ping"))
|
|
+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.ping.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ String output = String.format(PurpurConfig.pingCommandOutput, player.getGameProfile().getName(), player.latency);
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2621e54879e9ab0029a875f1d09eee67878b90d5
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java
|
|
@@ -0,0 +1,66 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.bukkit.ChatColor;
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+
|
|
+import java.io.File;
|
|
+import java.util.Collections;
|
|
+import java.util.List;
|
|
+import java.util.stream.Collectors;
|
|
+import java.util.stream.Stream;
|
|
+
|
|
+public class PurpurCommand extends Command {
|
|
+ public PurpurCommand(String name) {
|
|
+ super(name);
|
|
+ this.description = "Purpur related commands";
|
|
+ this.usageMessage = "/purpur [reload | version]";
|
|
+ this.setPermission("bukkit.command.purpur");
|
|
+ }
|
|
+
|
|
+ @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;
|
|
+
|
|
+ if (args.length != 1) {
|
|
+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (args[0].equalsIgnoreCase("reload")) {
|
|
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
|
|
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
|
|
+
|
|
+ MinecraftServer console = MinecraftServer.getServer();
|
|
+ PurpurConfig.init((File) console.options.valueOf("purpur-settings"));
|
|
+ for (ServerLevel level : console.getAllLevels()) {
|
|
+ level.purpurConfig.init();
|
|
+ level.resetBreedingCooldowns();
|
|
+ }
|
|
+ console.server.reloadCount++;
|
|
+
|
|
+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Purpur config reload complete.");
|
|
+ } else if (args[0].equalsIgnoreCase("version")) {
|
|
+ Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
|
|
+ if (verCmd != null) {
|
|
+ return verCmd.execute(sender, commandLabel, new String[0]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java b/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2852c07adb080c34905f5d1b19efed8ea47eecc6
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java
|
|
@@ -0,0 +1,44 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.purpurmc.purpur.task.RamBarTask;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class RamBarCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("rambar")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.rambar"))
|
|
+ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.rambar.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ boolean result = RamBarTask.instance().togglePlayer(player.getBukkitEntity());
|
|
+ player.ramBar(result);
|
|
+
|
|
+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.rambarCommandOutput,
|
|
+ Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off")
|
|
+ .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)),
|
|
+ Placeholder.parsed("target", player.getGameProfile().getName()));
|
|
+
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/RamCommand.java b/src/main/java/org/purpurmc/purpur/command/RamCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4ea0877f92b6733035d83a186c3d02c101c9b6cd
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/RamCommand.java
|
|
@@ -0,0 +1,30 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.purpurmc.purpur.task.RamBarTask;
|
|
+
|
|
+public class RamCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("ram")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.ram"))
|
|
+ .executes(context -> {
|
|
+ CommandSourceStack sender = context.getSource();
|
|
+ RamBarTask ramBar = RamBarTask.instance();
|
|
+ sender.sendSuccess(PaperAdventure.asVanilla(MiniMessage.miniMessage().deserialize(PurpurConfig.ramCommandOutput,
|
|
+ Placeholder.component("allocated", ramBar.format(ramBar.getAllocated())),
|
|
+ Placeholder.component("used", ramBar.format(ramBar.getUsed())),
|
|
+ Placeholder.component("xmx", ramBar.format(ramBar.getXmx())),
|
|
+ Placeholder.component("xms", ramBar.format(ramBar.getXms())),
|
|
+ Placeholder.unparsed("percent", ((int) (ramBar.getPercent() * 100)) + "%")
|
|
+ )), false);
|
|
+ return 1;
|
|
+ })
|
|
+ );
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java b/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..d8f9b044107ff7c29a83eb5378aa9f5465ba1995
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java
|
|
@@ -0,0 +1,44 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.commands.arguments.EntityArgument;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.purpurmc.purpur.task.TPSBarTask;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class TPSBarCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("tpsbar")
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.tpsbar"))
|
|
+ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException())))
|
|
+ .then(Commands.argument("targets", EntityArgument.players())
|
|
+ .requires(listener -> listener.hasPermission(2, "bukkit.command.tpsbar.other"))
|
|
+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets")))
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender, Collection<ServerPlayer> targets) {
|
|
+ for (ServerPlayer player : targets) {
|
|
+ boolean result = TPSBarTask.instance().togglePlayer(player.getBukkitEntity());
|
|
+ player.tpsBar(result);
|
|
+
|
|
+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.tpsbarCommandOutput,
|
|
+ Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off")
|
|
+ .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)),
|
|
+ Placeholder.parsed("target", player.getGameProfile().getName()));
|
|
+
|
|
+ sender.sendSuccess(output, false);
|
|
+ }
|
|
+ return targets.size();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java b/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4bb475099bcf8f05d5f1474e7fbf29c57c2c40cd
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java
|
|
@@ -0,0 +1,55 @@
|
|
+package org.purpurmc.purpur.command;
|
|
+
|
|
+import com.mojang.brigadier.CommandDispatcher;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import net.minecraft.commands.CommandSourceStack;
|
|
+import net.minecraft.commands.Commands;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.util.concurrent.TimeUnit;
|
|
+import java.util.function.Function;
|
|
+
|
|
+public class UptimeCommand {
|
|
+ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
+ dispatcher.register(Commands.literal("uptime")
|
|
+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.uptime"))
|
|
+ .executes((context) -> execute(context.getSource()))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static int execute(CommandSourceStack sender) {
|
|
+ Data data = new Data();
|
|
+
|
|
+ data.format = PurpurConfig.uptimeFormat;
|
|
+ data.hide = true;
|
|
+ data.millis = System.currentTimeMillis() - MinecraftServer.startTimeMillis;
|
|
+
|
|
+ process(data, "<days>", PurpurConfig.uptimeDay, PurpurConfig.uptimeDays, TimeUnit.DAYS, TimeUnit.MILLISECONDS::toDays);
|
|
+ process(data, "<hours>", PurpurConfig.uptimeHour, PurpurConfig.uptimeHours, TimeUnit.HOURS, TimeUnit.MILLISECONDS::toHours);
|
|
+ process(data, "<minutes>", PurpurConfig.uptimeMinute, PurpurConfig.uptimeMinutes, TimeUnit.MINUTES, TimeUnit.MILLISECONDS::toMinutes);
|
|
+ data.hide = false; // never hide seconds
|
|
+ process(data, "<seconds>", PurpurConfig.uptimeSecond, PurpurConfig.uptimeSeconds, TimeUnit.SECONDS, TimeUnit.MILLISECONDS::toSeconds);
|
|
+
|
|
+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.uptimeCommandOutput, Placeholder.unparsed("uptime", data.format));
|
|
+ sender.sendSuccess(output, false);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ private static void process(Data data, String replace, String singular, String plural, TimeUnit unit, Function<Long, Long> func) {
|
|
+ if (data.format.contains(replace)) {
|
|
+ long val = func.apply(data.millis);
|
|
+ if (data.hide) data.hide = val == 0;
|
|
+ if (!data.hide) data.millis -= unit.toMillis(val);
|
|
+ data.format = data.format.replace(replace, data.hide ? "" : String.format(val == 1 ? singular : plural, val));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static class Data {
|
|
+ String format;
|
|
+ boolean hide;
|
|
+ long millis;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ed494e0ad278813a0eb261101447b84cca3ad7aa
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java
|
|
@@ -0,0 +1,71 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+
|
|
+public class FlyingMoveControllerWASD extends MoveControllerWASD {
|
|
+ protected final float groundSpeedModifier;
|
|
+ protected final float flyingSpeedModifier;
|
|
+ protected int tooHighCooldown = 0;
|
|
+ protected boolean setNoGravityFlag;
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity) {
|
|
+ this(entity, 1.0F);
|
|
+ }
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier) {
|
|
+ this(entity, groundSpeedModifier, 1.0F, true);
|
|
+ }
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier) {
|
|
+ this(entity, groundSpeedModifier, flyingSpeedModifier, true);
|
|
+ }
|
|
+
|
|
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier, boolean setNoGravityFlag) {
|
|
+ super(entity);
|
|
+ this.groundSpeedModifier = groundSpeedModifier;
|
|
+ this.flyingSpeedModifier = flyingSpeedModifier;
|
|
+ this.setNoGravityFlag = setNoGravityFlag;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ float forward = Math.max(0.0F, rider.getForwardMot());
|
|
+ float vertical = forward == 0.0F ? 0.0F : -(rider.xRotO / 45.0F);
|
|
+ float strafe = rider.getStrafeMot();
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity)) {
|
|
+ entity.onSpacebar();
|
|
+ }
|
|
+
|
|
+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
|
|
+ if (tooHighCooldown <= 0) {
|
|
+ tooHighCooldown = 20;
|
|
+ }
|
|
+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.05D, 0.0D));
|
|
+ vertical = 0.0F;
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
+ float speed = (float) getSpeedModifier();
|
|
+
|
|
+ if (entity.onGround) {
|
|
+ speed *= groundSpeedModifier; // TODO = fix this!
|
|
+ } else {
|
|
+ speed *= flyingSpeedModifier;
|
|
+ }
|
|
+
|
|
+ if (setNoGravityFlag) {
|
|
+ entity.setNoGravity(forward > 0);
|
|
+ }
|
|
+
|
|
+ entity.setSpeed(speed);
|
|
+ entity.setVerticalMot(vertical);
|
|
+ entity.setStrafeMot(strafe);
|
|
+ entity.setForwardMot(forward);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..9383c07fa53141127106a1f289366a040960d52e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java
|
|
@@ -0,0 +1,63 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+
|
|
+public class FlyingWithSpacebarMoveControllerWASD extends FlyingMoveControllerWASD {
|
|
+ public FlyingWithSpacebarMoveControllerWASD(Mob entity) {
|
|
+ super(entity);
|
|
+ }
|
|
+
|
|
+ public FlyingWithSpacebarMoveControllerWASD(Mob entity, float groundSpeedModifier) {
|
|
+ super(entity, groundSpeedModifier);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ float forward = rider.getForwardMot();
|
|
+ float strafe = rider.getStrafeMot() * 0.5F;
|
|
+ float vertical = 0;
|
|
+
|
|
+ if (forward < 0.0F) {
|
|
+ forward *= 0.5F;
|
|
+ strafe *= 0.5F;
|
|
+ }
|
|
+
|
|
+ float speed = (float) entity.getAttributeValue(Attributes.MOVEMENT_SPEED);
|
|
+
|
|
+ if (entity.onGround) {
|
|
+ speed *= groundSpeedModifier;
|
|
+ }
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar()) {
|
|
+ entity.setNoGravity(true);
|
|
+ vertical = 1.0F;
|
|
+ } else {
|
|
+ entity.setNoGravity(false);
|
|
+ }
|
|
+
|
|
+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
|
|
+ if (tooHighCooldown <= 0) {
|
|
+ tooHighCooldown = 20;
|
|
+ }
|
|
+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.2D, 0.0D));
|
|
+ vertical = 0.0F;
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(speed);
|
|
+ entity.setSpeed((float) getSpeedModifier());
|
|
+ entity.setVerticalMot(vertical);
|
|
+ entity.setStrafeMot(strafe);
|
|
+ entity.setForwardMot(forward);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+
|
|
+ Vec3 mot = entity.getDeltaMovement();
|
|
+ if (mot.y > 0.2D) {
|
|
+ entity.setDeltaMovement(mot.x, 0.2D, mot.z);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b8c25c96e95dd5ec3ad9fa4c41bd6c08e144832d
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java
|
|
@@ -0,0 +1,76 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+
|
|
+import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.control.LookControl;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+
|
|
+public class LookControllerWASD extends LookControl {
|
|
+ protected final Mob entity;
|
|
+ private float yOffset = 0;
|
|
+ private float xOffset = 0;
|
|
+
|
|
+ public LookControllerWASD(Mob entity) {
|
|
+ super(entity);
|
|
+ this.entity = entity;
|
|
+ }
|
|
+
|
|
+ // tick
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ protected void purpurTick(Player rider) {
|
|
+ setYawPitch(rider.getYRot(), rider.getXRot());
|
|
+ }
|
|
+
|
|
+ public void vanillaTick() {
|
|
+ super.tick();
|
|
+ }
|
|
+
|
|
+ public void setYawPitch(float yRot, float xRot) {
|
|
+ entity.setXRot(normalizePitch(xRot + xOffset));
|
|
+ entity.setYRot(normalizeYaw(yRot + yOffset));
|
|
+ entity.setYHeadRot(entity.getYRot());
|
|
+ entity.xRotO = entity.getXRot();
|
|
+ entity.yRotO = entity.getYRot();
|
|
+
|
|
+ entity.tracker.broadcast(new ClientboundMoveEntityPacket
|
|
+ .PosRot(entity.getId(),
|
|
+ (short) 0, (short) 0, (short) 0,
|
|
+ (byte) Mth.floor(entity.getYRot() * 256.0F / 360.0F),
|
|
+ (byte) Mth.floor(entity.getXRot() * 256.0F / 360.0F),
|
|
+ entity.onGround));
|
|
+ }
|
|
+
|
|
+ public void setOffsets(float yaw, float pitch) {
|
|
+ yOffset = yaw;
|
|
+ xOffset = pitch;
|
|
+ }
|
|
+
|
|
+ public float normalizeYaw(float yaw) {
|
|
+ yaw %= 360.0f;
|
|
+ if (yaw >= 180.0f) {
|
|
+ yaw -= 360.0f;
|
|
+ } else if (yaw < -180.0f) {
|
|
+ yaw += 360.0f;
|
|
+ }
|
|
+ return yaw;
|
|
+ }
|
|
+
|
|
+ public float normalizePitch(float pitch) {
|
|
+ if (pitch > 90.0f) {
|
|
+ pitch = 90.0f;
|
|
+ } else if (pitch < -90.0f) {
|
|
+ pitch = -90.0f;
|
|
+ }
|
|
+ return pitch;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..21fd6ea2a482758a3016e3bc2cdebe2d89267481
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java
|
|
@@ -0,0 +1,89 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.ai.control.MoveControl;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import org.purpurmc.purpur.event.entity.RidableSpacebarEvent;
|
|
+
|
|
+public class MoveControllerWASD extends MoveControl {
|
|
+ protected final Mob entity;
|
|
+ private final double speedModifier;
|
|
+
|
|
+ public MoveControllerWASD(Mob entity) {
|
|
+ this(entity, 1.0D);
|
|
+ }
|
|
+
|
|
+ public MoveControllerWASD(Mob entity, double speedModifier) {
|
|
+ super(entity);
|
|
+ this.entity = entity;
|
|
+ this.speedModifier = speedModifier;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasWanted() {
|
|
+ return entity.getRider() != null ? strafeForwards != 0 || strafeRight != 0 : super.hasWanted();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ if (entity.getRider() != null && entity.isControllable()) {
|
|
+ purpurTick(entity.getRider());
|
|
+ } else {
|
|
+ vanillaTick();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void vanillaTick() {
|
|
+ super.tick();
|
|
+ }
|
|
+
|
|
+ public void purpurTick(Player rider) {
|
|
+ float forward = rider.getForwardMot() * 0.5F;
|
|
+ float strafe = rider.getStrafeMot() * 0.25F;
|
|
+
|
|
+ if (forward <= 0.0F) {
|
|
+ forward *= 0.5F;
|
|
+ }
|
|
+
|
|
+ float yawOffset = 0;
|
|
+ if (strafe != 0) {
|
|
+ if (forward == 0) {
|
|
+ yawOffset += strafe > 0 ? -90 : 90;
|
|
+ forward = Math.abs(strafe * 2);
|
|
+ } else {
|
|
+ yawOffset += strafe > 0 ? -30 : 30;
|
|
+ strafe /= 2;
|
|
+ if (forward < 0) {
|
|
+ yawOffset += strafe > 0 ? -110 : 110;
|
|
+ forward *= -1;
|
|
+ }
|
|
+ }
|
|
+ } else if (forward < 0) {
|
|
+ yawOffset -= 180;
|
|
+ forward *= -1;
|
|
+ }
|
|
+
|
|
+ ((LookControllerWASD) entity.getLookControl()).setOffsets(yawOffset, 0);
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar() && entity.onGround) {
|
|
+ entity.jumpFromGround();
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
|
|
+
|
|
+ entity.setSpeed((float) getSpeedModifier());
|
|
+ entity.setForwardMot(forward);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+ }
|
|
+
|
|
+ public static boolean spacebarEvent(Mob entity) {
|
|
+ if (RidableSpacebarEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
+ return new RidableSpacebarEvent(entity.getBukkitEntity()).callEvent();
|
|
+ } else {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ba2a37dad43e238e54632975abea8ee6fafaa9e0
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java
|
|
@@ -0,0 +1,50 @@
|
|
+package org.purpurmc.purpur.controller;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+
|
|
+public class WaterMoveControllerWASD extends MoveControllerWASD {
|
|
+ private final double speedModifier;
|
|
+
|
|
+ public WaterMoveControllerWASD(Mob entity) {
|
|
+ this(entity, 1.0D);
|
|
+ }
|
|
+
|
|
+ public WaterMoveControllerWASD(Mob entity, double speedModifier) {
|
|
+ super(entity);
|
|
+ this.speedModifier = speedModifier;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void purpurTick(Player rider) {
|
|
+ float forward = rider.getForwardMot();
|
|
+ float strafe = rider.getStrafeMot() * 0.5F; // strafe slower by default
|
|
+ float vertical = -(rider.xRotO / 90);
|
|
+
|
|
+ if (forward == 0.0F) {
|
|
+ // strafe slower if not moving forward
|
|
+ strafe *= 0.5F;
|
|
+ // do not move vertically if not moving forward
|
|
+ vertical = 0.0F;
|
|
+ } else if (forward < 0.0F) {
|
|
+ // water animals can't swim backwards
|
|
+ forward = 0.0F;
|
|
+ vertical = 0.0F;
|
|
+ }
|
|
+
|
|
+ if (rider.jumping && spacebarEvent(entity)) {
|
|
+ entity.onSpacebar();
|
|
+ }
|
|
+
|
|
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
|
|
+ entity.setSpeed((float) getSpeedModifier() * 0.1F);
|
|
+
|
|
+ entity.setForwardMot(forward * (float) speedModifier);
|
|
+ entity.setStrafeMot(strafe * (float) speedModifier);
|
|
+ entity.setVerticalMot(vertical * (float) speedModifier);
|
|
+
|
|
+ setForward(entity.getForwardMot());
|
|
+ setStrafe(entity.getStrafeMot());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f0279d6cdc93f524f321c3c40967fdeeb8d2c46b
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java
|
|
@@ -0,0 +1,104 @@
|
|
+package org.purpurmc.purpur.entity;
|
|
+
|
|
+import net.minecraft.core.particles.ParticleTypes;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.damagesource.DamageSource;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.entity.animal.Dolphin;
|
|
+import net.minecraft.world.entity.projectile.LlamaSpit;
|
|
+import net.minecraft.world.entity.projectile.ProjectileUtil;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.phys.BlockHitResult;
|
|
+import net.minecraft.world.phys.EntityHitResult;
|
|
+import net.minecraft.world.phys.HitResult;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+
|
|
+public class DolphinSpit extends LlamaSpit {
|
|
+ public LivingEntity dolphin;
|
|
+ public int ticksLived;
|
|
+
|
|
+ public DolphinSpit(EntityType<? extends LlamaSpit> type, Level world) {
|
|
+ super(type, world);
|
|
+ }
|
|
+
|
|
+ public DolphinSpit(Level world, Dolphin dolphin) {
|
|
+ this(EntityType.LLAMA_SPIT, world);
|
|
+ setOwner(dolphin.getRider() != null ? dolphin.getRider() : dolphin);
|
|
+ this.dolphin = dolphin;
|
|
+ this.setPos(
|
|
+ dolphin.getX() - (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(dolphin.yBodyRot * 0.017453292F),
|
|
+ dolphin.getEyeY() - 0.10000000149011612D,
|
|
+ dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(dolphin.yBodyRot * 0.017453292F));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canSaveToDisk() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public void tick() {
|
|
+ super_tick();
|
|
+
|
|
+ Vec3 mot = this.getDeltaMovement();
|
|
+ HitResult hitResult = ProjectileUtil.getHitResult(this, this::canHitEntity);
|
|
+
|
|
+ this.preOnHit(hitResult);
|
|
+
|
|
+ double x = this.getX() + mot.x;
|
|
+ double y = this.getY() + mot.y;
|
|
+ double z = this.getZ() + mot.z;
|
|
+
|
|
+ this.updateRotation();
|
|
+
|
|
+ Vec3 motDouble = mot.scale(2.0);
|
|
+ for (int i = 0; i < 5; i++) {
|
|
+ ((ServerLevel) level).sendParticles(null, ParticleTypes.BUBBLE,
|
|
+ getX() + random.nextFloat() / 2 - 0.25F,
|
|
+ getY() + random.nextFloat() / 2 - 0.25F,
|
|
+ getZ() + random.nextFloat() / 2 - 0.25F,
|
|
+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true);
|
|
+ }
|
|
+
|
|
+ if (++ticksLived > 20) {
|
|
+ this.discard();
|
|
+ } else {
|
|
+ this.setDeltaMovement(mot.scale(0.99D));
|
|
+ if (!this.isNoGravity()) {
|
|
+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
|
|
+ }
|
|
+
|
|
+ this.setPos(x, y, z);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void shoot(double x, double y, double z, float speed, float inaccuracy) {
|
|
+ setDeltaMovement(new Vec3(x, y, z).normalize().add(
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
|
|
+ .scale(speed));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitEntity(EntityHitResult entityHitResult) {
|
|
+ Entity shooter = this.getOwner();
|
|
+ if (shooter instanceof LivingEntity) {
|
|
+ entityHitResult.getEntity().hurt(entityHitResult.getEntity().damageSources().mobProjectile(this, (LivingEntity) shooter), level.purpurConfig.dolphinSpitDamage);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitBlock(BlockHitResult blockHitResult) {
|
|
+ if (this.hitCancelled) {
|
|
+ return;
|
|
+ }
|
|
+ BlockState state = this.level.getBlockState(blockHitResult.getBlockPos());
|
|
+ state.onProjectileHit(this.level, state, blockHitResult, this);
|
|
+ this.discard();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/GlowSquidColor.java b/src/main/java/org/purpurmc/purpur/entity/GlowSquidColor.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c90256f4c16ffdb2d8e767e837ea36ac7a6613be
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/GlowSquidColor.java
|
|
@@ -0,0 +1,61 @@
|
|
+package org.purpurmc.purpur.entity;
|
|
+
|
|
+import net.minecraft.util.RandomSource;
|
|
+
|
|
+import java.util.ArrayList;
|
|
+import java.util.Arrays;
|
|
+import java.util.HashMap;
|
|
+import java.util.List;
|
|
+import java.util.Locale;
|
|
+import java.util.Map;
|
|
+
|
|
+public enum GlowSquidColor {
|
|
+ BLUE, RED, GREEN, PINK, YELLOW, ORANGE, INDIGO, PURPLE, WHITE, GRAY, BLACK;
|
|
+
|
|
+ @Override
|
|
+ public String toString() {
|
|
+ return this.name().toLowerCase(Locale.ROOT);
|
|
+ }
|
|
+
|
|
+ public enum Mode {
|
|
+ RAINBOW(RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, PURPLE),
|
|
+ ALL_COLORS(BLUE, RED, GREEN, PINK, YELLOW, ORANGE, INDIGO, PURPLE, WHITE, GRAY, BLACK),
|
|
+ TRANS_PRIDE(BLUE, WHITE, PINK),
|
|
+ LESBIAN_PRIDE(RED, ORANGE, WHITE, PINK, PURPLE),
|
|
+ BI_PRIDE(BLUE, PINK, PURPLE),
|
|
+ GAY_PRIDE(BLUE, GREEN, WHITE),
|
|
+ PAN_PRIDE(PINK, YELLOW, BLUE),
|
|
+ ACE_PRIDE(BLACK, GRAY, WHITE, PURPLE),
|
|
+ ARO_PRIDE(BLACK, GRAY, WHITE, GREEN),
|
|
+ ENBY_PRIDE(YELLOW, WHITE, BLACK, PURPLE),
|
|
+ GENDERFLUID(PURPLE, WHITE, BLACK, PINK, BLUE),
|
|
+ MONOCHROME(BLACK, GRAY, WHITE),
|
|
+ VANILLA(BLUE);
|
|
+
|
|
+ private static final Map<String, Mode> BY_NAME = new HashMap<>();
|
|
+
|
|
+ static {
|
|
+ Arrays.stream(values()).forEach(mode -> BY_NAME.put(mode.name(), mode));
|
|
+ }
|
|
+
|
|
+ private final List<GlowSquidColor> colors = new ArrayList<>();
|
|
+
|
|
+ Mode(GlowSquidColor... colors) {
|
|
+ this.colors.addAll(Arrays.stream(colors).toList());
|
|
+ }
|
|
+
|
|
+ public static Mode get(String string) {
|
|
+ Mode mode = BY_NAME.get(string.toUpperCase(Locale.ROOT));
|
|
+ return mode == null ? RAINBOW : mode;
|
|
+ }
|
|
+
|
|
+ public GlowSquidColor getRandom(RandomSource random) {
|
|
+ return this.colors.get(random.nextInt(this.colors.size()));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String toString() {
|
|
+ return this.name().toLowerCase(Locale.ROOT);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1542f038621b97a298a0fb31ab3be912e2bcd0d6
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java
|
|
@@ -0,0 +1,119 @@
|
|
+package org.purpurmc.purpur.entity;
|
|
+
|
|
+import net.minecraft.core.particles.ParticleTypes;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.damagesource.DamageSource;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.entity.decoration.ArmorStand;
|
|
+import net.minecraft.world.entity.monster.Phantom;
|
|
+import net.minecraft.world.entity.projectile.LlamaSpit;
|
|
+import net.minecraft.world.entity.projectile.ProjectileUtil;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.phys.BlockHitResult;
|
|
+import net.minecraft.world.phys.EntityHitResult;
|
|
+import net.minecraft.world.phys.HitResult;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+
|
|
+public class PhantomFlames extends LlamaSpit {
|
|
+ public Phantom phantom;
|
|
+ public int ticksLived;
|
|
+ public boolean canGrief = false;
|
|
+
|
|
+ public PhantomFlames(EntityType<? extends LlamaSpit> type, Level world) {
|
|
+ super(type, world);
|
|
+ }
|
|
+
|
|
+ public PhantomFlames(Level world, Phantom phantom) {
|
|
+ this(EntityType.LLAMA_SPIT, world);
|
|
+ setOwner(phantom.getRider() != null ? phantom.getRider() : phantom);
|
|
+ this.phantom = phantom;
|
|
+ this.setPos(
|
|
+ phantom.getX() - (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(phantom.yBodyRot * 0.017453292F),
|
|
+ phantom.getEyeY() - 0.10000000149011612D,
|
|
+ phantom.getZ() + (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(phantom.yBodyRot * 0.017453292F));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canSaveToDisk() {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public void tick() {
|
|
+ super_tick();
|
|
+
|
|
+ Vec3 mot = this.getDeltaMovement();
|
|
+ HitResult hitResult = ProjectileUtil.getHitResult(this, this::canHitEntity);
|
|
+
|
|
+ this.preOnHit(hitResult);
|
|
+
|
|
+ double x = this.getX() + mot.x;
|
|
+ double y = this.getY() + mot.y;
|
|
+ double z = this.getZ() + mot.z;
|
|
+
|
|
+ this.updateRotation();
|
|
+
|
|
+ Vec3 motDouble = mot.scale(2.0);
|
|
+ for (int i = 0; i < 5; i++) {
|
|
+ ((ServerLevel) level).sendParticles(null, ParticleTypes.FLAME,
|
|
+ getX() + random.nextFloat() / 2 - 0.25F,
|
|
+ getY() + random.nextFloat() / 2 - 0.25F,
|
|
+ getZ() + random.nextFloat() / 2 - 0.25F,
|
|
+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true);
|
|
+ }
|
|
+
|
|
+ if (++ticksLived > 20) {
|
|
+ this.discard();
|
|
+ } else if (this.level.getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) {
|
|
+ this.discard();
|
|
+ } else if (this.isInWaterOrBubble()) {
|
|
+ this.discard();
|
|
+ } else {
|
|
+ this.setDeltaMovement(mot.scale(0.99D));
|
|
+ if (!this.isNoGravity()) {
|
|
+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
|
|
+ }
|
|
+
|
|
+ this.setPos(x, y, z);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void shoot(double x, double y, double z, float speed, float inaccuracy) {
|
|
+ setDeltaMovement(new Vec3(x, y, z).normalize().add(
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
|
|
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
|
|
+ .scale(speed));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitEntity(EntityHitResult entityHitResult) {
|
|
+ Entity shooter = this.getOwner();
|
|
+ if (shooter instanceof LivingEntity) {
|
|
+ Entity target = entityHitResult.getEntity();
|
|
+ if (canGrief || (target instanceof LivingEntity && !(target instanceof ArmorStand))) {
|
|
+ boolean hurt = target.hurt(target.damageSources().mobProjectile(this, (LivingEntity) shooter), level.purpurConfig.phantomFlameDamage);
|
|
+ if (hurt && level.purpurConfig.phantomFlameFireTime > 0) {
|
|
+ target.setSecondsOnFire(level.purpurConfig.phantomFlameFireTime);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onHitBlock(BlockHitResult blockHitResult) {
|
|
+ if (this.hitCancelled) {
|
|
+ return;
|
|
+ }
|
|
+ if (this.canGrief) {
|
|
+ BlockState state = this.level.getBlockState(blockHitResult.getBlockPos());
|
|
+ state.onProjectileHit(this.level, state, blockHitResult, this);
|
|
+ }
|
|
+ this.discard();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8babdaddd8b33278aea0369dbbeeb445abe45016
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java
|
|
@@ -0,0 +1,20 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.world.entity.Mob;
|
|
+import net.minecraft.world.entity.ai.goal.Goal;
|
|
+
|
|
+import java.util.EnumSet;
|
|
+
|
|
+public class HasRider extends Goal {
|
|
+ public final Mob entity;
|
|
+
|
|
+ public HasRider(Mob entity) {
|
|
+ this.entity = entity;
|
|
+ setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.TARGET, Flag.UNKNOWN_BEHAVIOR));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return entity.getRider() != null && entity.isControllable();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..432f4f3d82af2f19820890b68d33189a9f2c69f9
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java
|
|
@@ -0,0 +1,17 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.world.entity.animal.horse.AbstractHorse;
|
|
+
|
|
+public class HorseHasRider extends HasRider {
|
|
+ public final AbstractHorse horse;
|
|
+
|
|
+ public HorseHasRider(AbstractHorse entity) {
|
|
+ super(entity);
|
|
+ this.horse = entity;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return super.canUse() && horse.isSaddled();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..18a95e043cbffa65eeaaf65ff7695e5dc939820c
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java
|
|
@@ -0,0 +1,17 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.world.entity.animal.horse.Llama;
|
|
+
|
|
+public class LlamaHasRider extends HasRider {
|
|
+ public final Llama llama;
|
|
+
|
|
+ public LlamaHasRider(Llama entity) {
|
|
+ super(entity);
|
|
+ this.llama = entity;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return super.canUse() && llama.isSaddled() && llama.isControllable();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java b/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..115a3b36cbb7716b28ef940a29ca97ac42a8a521
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java
|
|
@@ -0,0 +1,91 @@
|
|
+package org.purpurmc.purpur.entity.ai;
|
|
+
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.InteractionHand;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.ai.goal.Goal;
|
|
+import net.minecraft.world.entity.animal.IronGolem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+
|
|
+import java.util.EnumSet;
|
|
+import java.util.UUID;
|
|
+
|
|
+public class ReceiveFlower extends Goal {
|
|
+ private final IronGolem irongolem;
|
|
+ private ServerPlayer target;
|
|
+ private int cooldown;
|
|
+
|
|
+ public ReceiveFlower(IronGolem entity) {
|
|
+ this.irongolem = entity;
|
|
+ setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ if (this.irongolem.getOfferFlowerTick() > 0) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!this.irongolem.isAngry()) {
|
|
+ return false;
|
|
+ }
|
|
+ UUID uuid = this.irongolem.getPersistentAngerTarget();
|
|
+ if (uuid == null) {
|
|
+ return false;
|
|
+ }
|
|
+ Entity target = ((ServerLevel) this.irongolem.level).getEntity(uuid);
|
|
+ if (!(target instanceof ServerPlayer player)) {
|
|
+ return false;
|
|
+ }
|
|
+ InteractionHand hand = getPoppyHand(player);
|
|
+ if (hand == null) {
|
|
+ return false;
|
|
+ }
|
|
+ removeFlower(player, hand);
|
|
+ this.target = player;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ return this.cooldown > 0;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ this.cooldown = 100;
|
|
+ this.irongolem.stopBeingAngry();
|
|
+ this.irongolem.offerFlower(true);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void stop() {
|
|
+ this.irongolem.offerFlower(false);
|
|
+ this.target = null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ this.irongolem.getLookControl().setLookAt(this.target, 30.0F, 30.0F);
|
|
+ --this.cooldown;
|
|
+ }
|
|
+
|
|
+ private InteractionHand getPoppyHand(ServerPlayer player) {
|
|
+ if (isPoppy(player.getMainHandItem())) {
|
|
+ return InteractionHand.MAIN_HAND;
|
|
+ }
|
|
+ if (isPoppy(player.getOffhandItem())) {
|
|
+ return InteractionHand.OFF_HAND;
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ private void removeFlower(ServerPlayer player, InteractionHand hand) {
|
|
+ player.setItemInHand(hand, ItemStack.EMPTY);
|
|
+ }
|
|
+
|
|
+ private boolean isPoppy(ItemStack item) {
|
|
+ return item.getItem() == Blocks.POPPY.asItem();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/gui/GUIColor.java b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0f2e7e0b81620c8581949bd5f0bdb567cd93c17e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java
|
|
@@ -0,0 +1,54 @@
|
|
+package org.purpurmc.purpur.gui;
|
|
+
|
|
+import net.md_5.bungee.api.ChatColor;
|
|
+
|
|
+import java.awt.Color;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+
|
|
+public enum GUIColor {
|
|
+ BLACK(ChatColor.BLACK, new Color(0x000000)),
|
|
+ DARK_BLUE(ChatColor.DARK_BLUE, new Color(0x0000AA)),
|
|
+ DARK_GREEN(ChatColor.DARK_GREEN, new Color(0x00AA00)),
|
|
+ DARK_AQUA(ChatColor.DARK_AQUA, new Color(0x009999)),
|
|
+ DARK_RED(ChatColor.DARK_RED, new Color(0xAA0000)),
|
|
+ DARK_PURPLE(ChatColor.DARK_PURPLE, new Color(0xAA00AA)),
|
|
+ GOLD(ChatColor.GOLD, new Color(0xBB8800)),
|
|
+ GRAY(ChatColor.GRAY, new Color(0x888888)),
|
|
+ DARK_GRAY(ChatColor.DARK_GRAY, new Color(0x444444)),
|
|
+ BLUE(ChatColor.BLUE, new Color(0x5555FF)),
|
|
+ GREEN(ChatColor.GREEN, new Color(0x55FF55)),
|
|
+ AQUA(ChatColor.AQUA, new Color(0x55DDDD)),
|
|
+ RED(ChatColor.RED, new Color(0xFF5555)),
|
|
+ LIGHT_PURPLE(ChatColor.LIGHT_PURPLE, new Color(0xFF55FF)),
|
|
+ YELLOW(ChatColor.YELLOW, new Color(0xFFBB00)),
|
|
+ WHITE(ChatColor.WHITE, new Color(0xBBBBBB));
|
|
+
|
|
+ private final ChatColor chat;
|
|
+ private final Color color;
|
|
+
|
|
+ private static final Map<ChatColor, GUIColor> BY_CHAT = new HashMap<>();
|
|
+
|
|
+ GUIColor(ChatColor chat, Color color) {
|
|
+ this.chat = chat;
|
|
+ this.color = color;
|
|
+ }
|
|
+
|
|
+ public Color getColor() {
|
|
+ return color;
|
|
+ }
|
|
+
|
|
+ public String getCode() {
|
|
+ return chat.toString();
|
|
+ }
|
|
+
|
|
+ public static GUIColor getColor(ChatColor chat) {
|
|
+ return BY_CHAT.get(chat);
|
|
+ }
|
|
+
|
|
+ static {
|
|
+ for (GUIColor color : values()) {
|
|
+ BY_CHAT.put(color.chat, color);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..33e89b4c00fa8318506b36cbe49fe4e412e0a9a1
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java
|
|
@@ -0,0 +1,78 @@
|
|
+package org.purpurmc.purpur.gui;
|
|
+
|
|
+import com.google.common.collect.Sets;
|
|
+import net.md_5.bungee.api.ChatColor;
|
|
+import net.md_5.bungee.api.chat.BaseComponent;
|
|
+import net.md_5.bungee.api.chat.TextComponent;
|
|
+
|
|
+import javax.swing.JTextPane;
|
|
+import javax.swing.Timer;
|
|
+import javax.swing.text.AttributeSet;
|
|
+import javax.swing.text.BadLocationException;
|
|
+import javax.swing.text.SimpleAttributeSet;
|
|
+import javax.swing.text.StyleConstants;
|
|
+import javax.swing.text.StyleContext;
|
|
+import java.util.Set;
|
|
+
|
|
+public class JColorTextPane extends JTextPane {
|
|
+ private static final GUIColor DEFAULT_COLOR = GUIColor.BLACK;
|
|
+
|
|
+ public void append(String msg) {
|
|
+ // TODO: update to use adventure instead
|
|
+ BaseComponent[] components = TextComponent.fromLegacyText(DEFAULT_COLOR.getCode() + msg, ChatColor.BLACK);
|
|
+ for (BaseComponent component : components) {
|
|
+ String text = component.toPlainText();
|
|
+ if (text == null || text.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ GUIColor guiColor = GUIColor.getColor(component.getColor());
|
|
+
|
|
+ StyleContext context = StyleContext.getDefaultStyleContext();
|
|
+ AttributeSet attr = context.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, guiColor.getColor());
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Bold, component.isBold() || guiColor != DEFAULT_COLOR);
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Italic, component.isItalic());
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Underline, component.isUnderlined());
|
|
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.StrikeThrough, component.isStrikethrough());
|
|
+ //attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Blink, component.isObfuscated()); // no such thing as Blink, sadly
|
|
+
|
|
+ try {
|
|
+ int pos = getDocument().getLength();
|
|
+ getDocument().insertString(pos, text, attr);
|
|
+
|
|
+ if (component.isObfuscated()) {
|
|
+ // dirty hack to blink some text
|
|
+ Blink blink = new Blink(pos, text.length(), attr, context.addAttribute(attr, StyleConstants.Foreground, getBackground()));
|
|
+ BLINKS.add(blink);
|
|
+ }
|
|
+ } catch (BadLocationException ignore) {
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final Set<Blink> BLINKS = Sets.newHashSet();
|
|
+ private static boolean SYNC_BLINK;
|
|
+
|
|
+ static {
|
|
+ new Timer(500, e -> {
|
|
+ SYNC_BLINK = !SYNC_BLINK;
|
|
+ BLINKS.forEach(Blink::blink);
|
|
+ }).start();
|
|
+ }
|
|
+
|
|
+ public class Blink {
|
|
+ private final int start, length;
|
|
+ private final AttributeSet attr1, attr2;
|
|
+
|
|
+ private Blink(int start, int length, AttributeSet attr1, AttributeSet attr2) {
|
|
+ this.start = start;
|
|
+ this.length = length;
|
|
+ this.attr1 = attr1;
|
|
+ this.attr2 = attr2;
|
|
+ }
|
|
+
|
|
+ private void blink() {
|
|
+ getStyledDocument().setCharacterAttributes(start, length, SYNC_BLINK ? attr1 : attr2, true);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java b/src/main/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..15a226e3854d731f7724025ea3459c8ace07630c
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java
|
|
@@ -0,0 +1,85 @@
|
|
+package org.purpurmc.purpur.gui.util;
|
|
+
|
|
+import org.apache.logging.log4j.Level;
|
|
+import org.apache.logging.log4j.core.LogEvent;
|
|
+import org.apache.logging.log4j.core.config.Configuration;
|
|
+import org.apache.logging.log4j.core.config.plugins.Plugin;
|
|
+import org.apache.logging.log4j.core.layout.PatternLayout;
|
|
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
|
|
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
|
|
+import org.apache.logging.log4j.core.pattern.PatternConverter;
|
|
+import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
|
+import org.apache.logging.log4j.core.pattern.PatternParser;
|
|
+import org.apache.logging.log4j.util.PerformanceSensitive;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+@Plugin(name = "highlightGUIError", category = PatternConverter.CATEGORY)
|
|
+@ConverterKeys({"highlightGUIError"})
|
|
+@PerformanceSensitive("allocation")
|
|
+public final class HighlightErrorConverter extends LogEventPatternConverter {
|
|
+ private static final String ERROR = "\u00A74\u00A7l"; // Bold Red
|
|
+ private static final String WARN = "\u00A7e\u00A7l"; // Bold Yellow
|
|
+
|
|
+ private final List<PatternFormatter> formatters;
|
|
+
|
|
+ private HighlightErrorConverter(List<PatternFormatter> formatters) {
|
|
+ super("highlightGUIError", null);
|
|
+ this.formatters = formatters;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void format(LogEvent event, StringBuilder toAppendTo) {
|
|
+ Level level = event.getLevel();
|
|
+ if (level.isMoreSpecificThan(Level.ERROR)) {
|
|
+ format(ERROR, event, toAppendTo);
|
|
+ return;
|
|
+ } else if (level.isMoreSpecificThan(Level.WARN)) {
|
|
+ format(WARN, event, toAppendTo);
|
|
+ return;
|
|
+ }
|
|
+ for (PatternFormatter formatter : formatters) {
|
|
+ formatter.format(event, toAppendTo);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void format(String style, LogEvent event, StringBuilder toAppendTo) {
|
|
+ int start = toAppendTo.length();
|
|
+ toAppendTo.append(style);
|
|
+ int end = toAppendTo.length();
|
|
+
|
|
+ for (PatternFormatter formatter : formatters) {
|
|
+ formatter.format(event, toAppendTo);
|
|
+ }
|
|
+
|
|
+ if (toAppendTo.length() == end) {
|
|
+ toAppendTo.setLength(start);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean handlesThrowable() {
|
|
+ for (final PatternFormatter formatter : formatters) {
|
|
+ if (formatter.handlesThrowable()) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public static HighlightErrorConverter newInstance(Configuration config, String[] options) {
|
|
+ if (options.length != 1) {
|
|
+ LOGGER.error("Incorrect number of options on highlightGUIError. Expected 1 received " + options.length);
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ if (options[0] == null) {
|
|
+ LOGGER.error("No pattern supplied on highlightGUIError");
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ PatternParser parser = PatternLayout.createPatternParser(config);
|
|
+ List<PatternFormatter> formatters = parser.parse(options[0]);
|
|
+ return new HighlightErrorConverter(formatters);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java b/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..7f526883495b3222746de3d0442e9e4fb5107036
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java
|
|
@@ -0,0 +1,26 @@
|
|
+package org.purpurmc.purpur.item;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import net.minecraft.world.effect.MobEffects;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.item.ItemNameBlockItem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import org.bukkit.event.entity.EntityPotionEffectEvent;
|
|
+
|
|
+public class GlowBerryItem extends ItemNameBlockItem {
|
|
+ public GlowBerryItem(Block block, Properties settings) {
|
|
+ super(block, settings);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemStack finishUsingItem(ItemStack stack, Level world, LivingEntity user) {
|
|
+ ItemStack result = super.finishUsingItem(stack, world, user);
|
|
+ if (world.purpurConfig.glowBerriesEatGlowDuration > 0 && user instanceof ServerPlayer player) {
|
|
+ player.addEffect(new MobEffectInstance(MobEffects.GLOWING, world.purpurConfig.glowBerriesEatGlowDuration), EntityPotionEffectEvent.Cause.FOOD);
|
|
+ }
|
|
+ return result;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java b/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c038fb2bbb0f0e78380bc24bbd6348b869669a90
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java
|
|
@@ -0,0 +1,36 @@
|
|
+package org.purpurmc.purpur.item;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.item.BlockItem;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+
|
|
+public class SpawnerItem extends BlockItem {
|
|
+
|
|
+ public SpawnerItem(Block block, Properties settings) {
|
|
+ super(block, settings);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected boolean updateCustomBlockEntityTag(BlockPos pos, Level level, Player player, ItemStack stack, BlockState state) {
|
|
+ boolean handled = super.updateCustomBlockEntityTag(pos, level, player, stack, state);
|
|
+ if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.place.spawners")) {
|
|
+ BlockEntity spawner = level.getBlockEntity(pos);
|
|
+ if (spawner instanceof SpawnerBlockEntity && stack.hasTag()) {
|
|
+ CompoundTag tag = stack.getTag();
|
|
+ if (tag.contains("Purpur.mob_type")) {
|
|
+ EntityType.byString(tag.getString("Purpur.mob_type")).ifPresent(type ->
|
|
+ ((SpawnerBlockEntity) spawner).getSpawner().setEntityId(type, level, level.random, pos));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return handled;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java b/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..055dd307e9d5ac0d4623c961164c84bab1edd3bd
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java
|
|
@@ -0,0 +1,81 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import com.google.common.io.ByteArrayDataInput;
|
|
+import com.google.common.io.ByteArrayDataOutput;
|
|
+import com.google.common.io.ByteStreams;
|
|
+import io.netty.buffer.Unpooled;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.network.FriendlyByteBuf;
|
|
+import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.bukkit.plugin.PluginBase;
|
|
+import org.bukkit.plugin.messaging.PluginMessageListener;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
+public class BeehiveTask implements PluginMessageListener {
|
|
+ public static final ResourceLocation BEEHIVE_C2S = new ResourceLocation("purpur", "beehive_c2s");
|
|
+ public static final ResourceLocation BEEHIVE_S2C = new ResourceLocation("purpur", "beehive_s2c");
|
|
+
|
|
+ private static BeehiveTask instance;
|
|
+
|
|
+ public static BeehiveTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new BeehiveTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ private final PluginBase plugin = new MinecraftInternalPlugin();
|
|
+
|
|
+ private BeehiveTask() {
|
|
+ }
|
|
+
|
|
+ public void register() {
|
|
+ Bukkit.getMessenger().registerOutgoingPluginChannel(this.plugin, BEEHIVE_S2C.toString());
|
|
+ Bukkit.getMessenger().registerIncomingPluginChannel(this.plugin, BEEHIVE_C2S.toString(), this);
|
|
+ }
|
|
+
|
|
+ public void unregister() {
|
|
+ Bukkit.getMessenger().unregisterOutgoingPluginChannel(this.plugin, BEEHIVE_S2C.toString());
|
|
+ Bukkit.getMessenger().unregisterIncomingPluginChannel(this.plugin, BEEHIVE_C2S.toString());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onPluginMessageReceived(@NotNull String channel, Player player, byte[] bytes) {
|
|
+ ByteArrayDataInput in = in(bytes);
|
|
+ long packedPos = in.readLong();
|
|
+ BlockPos pos = BlockPos.of(packedPos);
|
|
+
|
|
+ ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
|
|
+
|
|
+ BlockEntity blockEntity = serverPlayer.level.getBlockEntity(pos);
|
|
+ if (!(blockEntity instanceof BeehiveBlockEntity beehive)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ByteArrayDataOutput out = out();
|
|
+
|
|
+ out.writeInt(beehive.getOccupantCount());
|
|
+ out.writeLong(packedPos);
|
|
+
|
|
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(out.toByteArray()));
|
|
+ serverPlayer.connection.send(new ClientboundCustomPayloadPacket(BEEHIVE_S2C, buf));
|
|
+ }
|
|
+
|
|
+ @SuppressWarnings("UnstableApiUsage")
|
|
+ private static ByteArrayDataOutput out() {
|
|
+ return ByteStreams.newDataOutput();
|
|
+ }
|
|
+
|
|
+ @SuppressWarnings("UnstableApiUsage")
|
|
+ private static ByteArrayDataInput in(byte[] bytes) {
|
|
+ return ByteStreams.newDataInput(bytes);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/BossBarTask.java b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..114f273dd7f8b8a3c02f0651f6944859b33a65d4
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java
|
|
@@ -0,0 +1,121 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.bukkit.scheduler.BukkitRunnable;
|
|
+
|
|
+import java.util.HashMap;
|
|
+import java.util.HashSet;
|
|
+import java.util.Iterator;
|
|
+import java.util.Map;
|
|
+import java.util.UUID;
|
|
+
|
|
+public abstract class BossBarTask extends BukkitRunnable {
|
|
+ private final Map<UUID, BossBar> bossbars = new HashMap<>();
|
|
+ private boolean started;
|
|
+
|
|
+ abstract BossBar createBossBar();
|
|
+
|
|
+ abstract void updateBossBar(BossBar bossbar, Player player);
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ Iterator<Map.Entry<UUID, BossBar>> iter = bossbars.entrySet().iterator();
|
|
+ while (iter.hasNext()) {
|
|
+ Map.Entry<UUID, BossBar> entry = iter.next();
|
|
+ Player player = Bukkit.getPlayer(entry.getKey());
|
|
+ if (player == null) {
|
|
+ iter.remove();
|
|
+ continue;
|
|
+ }
|
|
+ updateBossBar(entry.getValue(), player);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void cancel() {
|
|
+ super.cancel();
|
|
+ new HashSet<>(this.bossbars.keySet()).forEach(uuid -> {
|
|
+ Player player = Bukkit.getPlayer(uuid);
|
|
+ if (player != null) {
|
|
+ removePlayer(player);
|
|
+ }
|
|
+ });
|
|
+ this.bossbars.clear();
|
|
+ }
|
|
+
|
|
+ public boolean removePlayer(Player player) {
|
|
+ BossBar bossbar = this.bossbars.remove(player.getUniqueId());
|
|
+ if (bossbar != null) {
|
|
+ player.hideBossBar(bossbar);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public void addPlayer(Player player) {
|
|
+ removePlayer(player);
|
|
+ BossBar bossbar = createBossBar();
|
|
+ this.bossbars.put(player.getUniqueId(), bossbar);
|
|
+ this.updateBossBar(bossbar, player);
|
|
+ player.showBossBar(bossbar);
|
|
+ }
|
|
+
|
|
+ public boolean hasPlayer(UUID uuid) {
|
|
+ return this.bossbars.containsKey(uuid);
|
|
+ }
|
|
+
|
|
+ public boolean togglePlayer(Player player) {
|
|
+ if (removePlayer(player)) {
|
|
+ return false;
|
|
+ }
|
|
+ addPlayer(player);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public void start() {
|
|
+ stop();
|
|
+ this.runTaskTimerAsynchronously(new MinecraftInternalPlugin(), 1, 1);
|
|
+ started = true;
|
|
+ }
|
|
+
|
|
+ public void stop() {
|
|
+ if (started) {
|
|
+ cancel();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void startAll() {
|
|
+ RamBarTask.instance().start();
|
|
+ TPSBarTask.instance().start();
|
|
+ CompassTask.instance().start();
|
|
+ }
|
|
+
|
|
+ public static void stopAll() {
|
|
+ RamBarTask.instance().stop();
|
|
+ TPSBarTask.instance().stop();
|
|
+ CompassTask.instance().stop();
|
|
+ }
|
|
+
|
|
+ public static void addToAll(ServerPlayer player) {
|
|
+ Player bukkit = player.getBukkitEntity();
|
|
+ if (player.ramBar()) {
|
|
+ RamBarTask.instance().addPlayer(bukkit);
|
|
+ }
|
|
+ if (player.tpsBar()) {
|
|
+ TPSBarTask.instance().addPlayer(bukkit);
|
|
+ }
|
|
+ if (player.compassBar()) {
|
|
+ CompassTask.instance().addPlayer(bukkit);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void removeFromAll(Player player) {
|
|
+ RamBarTask.instance().removePlayer(player);
|
|
+ TPSBarTask.instance().removePlayer(player);
|
|
+ CompassTask.instance().removePlayer(player);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/CompassTask.java b/src/main/java/org/purpurmc/purpur/task/CompassTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bece7eefc8ba8822b433835526251d2fb916c025
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/CompassTask.java
|
|
@@ -0,0 +1,68 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.world.item.Items;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+public class CompassTask extends BossBarTask {
|
|
+ private static CompassTask instance;
|
|
+
|
|
+ private int tick = 0;
|
|
+
|
|
+ public static CompassTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new CompassTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ if (++tick < PurpurConfig.commandCompassBarTickInterval) {
|
|
+ return;
|
|
+ }
|
|
+ tick = 0;
|
|
+
|
|
+ MinecraftServer.getServer().getAllLevels().forEach((level) -> {
|
|
+ if (level.purpurConfig.compassItemShowsBossBar) {
|
|
+ level.players().forEach(player -> {
|
|
+ if (!player.compassBar()) {
|
|
+ if (player.getMainHandItem().getItem() != Items.COMPASS && player.getOffhandItem().getItem() != Items.COMPASS) {
|
|
+ removePlayer(player.getBukkitEntity());
|
|
+ } else if (!hasPlayer(player.getUUID())) {
|
|
+ addPlayer(player.getBukkitEntity());
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ });
|
|
+
|
|
+ super.run();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ BossBar createBossBar() {
|
|
+ return BossBar.bossBar(Component.text(""), PurpurConfig.commandCompassBarProgressPercent, PurpurConfig.commandCompassBarProgressColor, PurpurConfig.commandCompassBarProgressOverlay);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ void updateBossBar(BossBar bossbar, Player player) {
|
|
+ float yaw = player.getLocation().getYaw();
|
|
+ int length = PurpurConfig.commandCompassBarTitle.length();
|
|
+ int pos = (int) ((normalize(yaw) * (length / 720F)) + (length / 2F));
|
|
+ bossbar.name(Component.text(PurpurConfig.commandCompassBarTitle.substring(pos - 25, pos + 25)));
|
|
+ }
|
|
+
|
|
+ private float normalize(float yaw) {
|
|
+ while (yaw < -180.0F) {
|
|
+ yaw += 360.0F;
|
|
+ }
|
|
+ while (yaw > 180.0F) {
|
|
+ yaw -= 360.0F;
|
|
+ }
|
|
+ return yaw;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/RamBarTask.java b/src/main/java/org/purpurmc/purpur/task/RamBarTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8e98c0ae73e2c40002a72b5d0d246ffa0c3ab38f
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/RamBarTask.java
|
|
@@ -0,0 +1,117 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+
|
|
+import java.lang.management.ManagementFactory;
|
|
+import java.lang.management.MemoryUsage;
|
|
+
|
|
+public class RamBarTask extends BossBarTask {
|
|
+ private static RamBarTask instance;
|
|
+ private long allocated = 0L;
|
|
+ private long used = 0L;
|
|
+ private long xmx = 0L;
|
|
+ private long xms = 0L;
|
|
+ private float percent = 0F;
|
|
+ private int tick = 0;
|
|
+
|
|
+ public static RamBarTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new RamBarTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ BossBar createBossBar() {
|
|
+ return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandRamBarProgressOverlay);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ void updateBossBar(BossBar bossbar, Player player) {
|
|
+ bossbar.progress(getBossBarProgress());
|
|
+ bossbar.color(getBossBarColor());
|
|
+ bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandRamBarTitle,
|
|
+ Placeholder.component("allocated", format(this.allocated)),
|
|
+ Placeholder.component("used", format(this.used)),
|
|
+ Placeholder.component("xmx", format(this.xmx)),
|
|
+ Placeholder.component("xms", format(this.xms)),
|
|
+ Placeholder.unparsed("percent", ((int) (this.percent * 100)) + "%")
|
|
+ ));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ if (++this.tick < PurpurConfig.commandRamBarTickInterval) {
|
|
+ return;
|
|
+ }
|
|
+ this.tick = 0;
|
|
+
|
|
+ MemoryUsage heap = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
|
|
+
|
|
+ this.allocated = heap.getCommitted();
|
|
+ this.used = heap.getUsed();
|
|
+ this.xmx = heap.getMax();
|
|
+ this.xms = heap.getInit();
|
|
+ this.percent = Math.max(Math.min((float) this.used / this.xmx, 1.0F), 0.0F);
|
|
+
|
|
+ super.run();
|
|
+ }
|
|
+
|
|
+ private float getBossBarProgress() {
|
|
+ return this.percent;
|
|
+ }
|
|
+
|
|
+ private BossBar.Color getBossBarColor() {
|
|
+ if (this.percent < 0.5F) {
|
|
+ return PurpurConfig.commandRamBarProgressColorGood;
|
|
+ } else if (this.percent < 0.75F) {
|
|
+ return PurpurConfig.commandRamBarProgressColorMedium;
|
|
+ } else {
|
|
+ return PurpurConfig.commandRamBarProgressColorLow;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public Component format(long v) {
|
|
+ String color;
|
|
+ if (this.percent < 0.60F) {
|
|
+ color = PurpurConfig.commandRamBarTextColorGood;
|
|
+ } else if (this.percent < 0.85F) {
|
|
+ color = PurpurConfig.commandRamBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandRamBarTextColorLow;
|
|
+ }
|
|
+ String value;
|
|
+ if (v < 1024) {
|
|
+ value = v + "B";
|
|
+ } else {
|
|
+ int z = (63 - Long.numberOfLeadingZeros(v)) / 10;
|
|
+ value = String.format("%.1f%s", (double) v / (1L << (z * 10)), "BKMGTPE".charAt(z));
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.unparsed("text", value));
|
|
+ }
|
|
+
|
|
+ public long getAllocated() {
|
|
+ return this.allocated;
|
|
+ }
|
|
+
|
|
+ public long getUsed() {
|
|
+ return this.used;
|
|
+ }
|
|
+
|
|
+ public long getXmx() {
|
|
+ return this.xmx;
|
|
+ }
|
|
+
|
|
+ public long getXms() {
|
|
+ return this.xms;
|
|
+ }
|
|
+
|
|
+ public float getPercent() {
|
|
+ return this.percent;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java b/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8769993e7ca59da309087051a3cd38fc562c15d1
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java
|
|
@@ -0,0 +1,142 @@
|
|
+package org.purpurmc.purpur.task;
|
|
+
|
|
+import net.kyori.adventure.bossbar.BossBar;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
|
+import org.purpurmc.purpur.PurpurConfig;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.entity.Player;
|
|
+
|
|
+public class TPSBarTask extends BossBarTask {
|
|
+ private static TPSBarTask instance;
|
|
+ private double tps = 20.0D;
|
|
+ private double mspt = 0.0D;
|
|
+ private int tick = 0;
|
|
+
|
|
+ public static TPSBarTask instance() {
|
|
+ if (instance == null) {
|
|
+ instance = new TPSBarTask();
|
|
+ }
|
|
+ return instance;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ BossBar createBossBar() {
|
|
+ return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandTPSBarProgressOverlay);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ void updateBossBar(BossBar bossbar, Player player) {
|
|
+ bossbar.progress(getBossBarProgress());
|
|
+ bossbar.color(getBossBarColor());
|
|
+ bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandTPSBarTitle,
|
|
+ Placeholder.component("tps", getTPSColor()),
|
|
+ Placeholder.component("mspt", getMSPTColor()),
|
|
+ Placeholder.component("ping", getPingColor(player.getPing()))
|
|
+ ));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ if (++tick < PurpurConfig.commandTPSBarTickInterval) {
|
|
+ return;
|
|
+ }
|
|
+ tick = 0;
|
|
+
|
|
+ this.tps = Math.max(Math.min(Bukkit.getTPS()[0], 20.0D), 0.0D);
|
|
+ this.mspt = Bukkit.getAverageTickTime();
|
|
+
|
|
+ super.run();
|
|
+ }
|
|
+
|
|
+ private float getBossBarProgress() {
|
|
+ if (PurpurConfig.commandTPSBarProgressFillMode == FillMode.MSPT) {
|
|
+ return Math.max(Math.min((float) mspt / 50.0F, 1.0F), 0.0F);
|
|
+ } else {
|
|
+ return Math.max(Math.min((float) tps / 20.0F, 1.0F), 0.0F);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private BossBar.Color getBossBarColor() {
|
|
+ if (isGood(PurpurConfig.commandTPSBarProgressFillMode)) {
|
|
+ return PurpurConfig.commandTPSBarProgressColorGood;
|
|
+ } else if (isMedium(PurpurConfig.commandTPSBarProgressFillMode)) {
|
|
+ return PurpurConfig.commandTPSBarProgressColorMedium;
|
|
+ } else {
|
|
+ return PurpurConfig.commandTPSBarProgressColorLow;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean isGood(FillMode mode) {
|
|
+ return isGood(mode, 0);
|
|
+ }
|
|
+
|
|
+ private boolean isGood(FillMode mode, int ping) {
|
|
+ if (mode == FillMode.MSPT) {
|
|
+ return mspt < 40;
|
|
+ } else if (mode == FillMode.TPS) {
|
|
+ return tps >= 19;
|
|
+ } else if (mode == FillMode.PING) {
|
|
+ return ping < 100;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean isMedium(FillMode mode) {
|
|
+ return isMedium(mode, 0);
|
|
+ }
|
|
+
|
|
+ private boolean isMedium(FillMode mode, int ping) {
|
|
+ if (mode == FillMode.MSPT) {
|
|
+ return mspt < 50;
|
|
+ } else if (mode == FillMode.TPS) {
|
|
+ return tps >= 15;
|
|
+ } else if (mode == FillMode.PING) {
|
|
+ return ping < 200;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private Component getTPSColor() {
|
|
+ String color;
|
|
+ if (isGood(FillMode.TPS)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorGood;
|
|
+ } else if (isMedium(FillMode.TPS)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandTPSBarTextColorLow;
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", tps)));
|
|
+ }
|
|
+
|
|
+ private Component getMSPTColor() {
|
|
+ String color;
|
|
+ if (isGood(FillMode.MSPT)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorGood;
|
|
+ } else if (isMedium(FillMode.MSPT)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandTPSBarTextColorLow;
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", mspt)));
|
|
+ }
|
|
+
|
|
+ private Component getPingColor(int ping) {
|
|
+ String color;
|
|
+ if (isGood(FillMode.PING, ping)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorGood;
|
|
+ } else if (isMedium(FillMode.PING, ping)) {
|
|
+ color = PurpurConfig.commandTPSBarTextColorMedium;
|
|
+ } else {
|
|
+ color = PurpurConfig.commandTPSBarTextColorLow;
|
|
+ }
|
|
+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%s", ping)));
|
|
+ }
|
|
+
|
|
+ public enum FillMode {
|
|
+ TPS, MSPT, PING
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Actionable.java b/src/main/java/org/purpurmc/purpur/tool/Actionable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e18c37f06730da9d3055d5215e813b1477c1e70e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Actionable.java
|
|
@@ -0,0 +1,24 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public abstract class Actionable {
|
|
+ private final Block into;
|
|
+ private final Map<Item, Double> drops;
|
|
+
|
|
+ public Actionable(Block into, Map<Item, Double> drops) {
|
|
+ this.into = into;
|
|
+ this.drops = drops;
|
|
+ }
|
|
+
|
|
+ public Block into() {
|
|
+ return into;
|
|
+ }
|
|
+
|
|
+ public Map<Item, Double> drops() {
|
|
+ return drops;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Strippable.java b/src/main/java/org/purpurmc/purpur/tool/Strippable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bf5402214f41af9c09bd6c5c4f45d330516d742e
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Strippable.java
|
|
@@ -0,0 +1,12 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public class Strippable extends Actionable {
|
|
+ public Strippable(Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Tillable.java b/src/main/java/org/purpurmc/purpur/tool/Tillable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..715f6dd44480347eebced43c11bc364e05727498
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Tillable.java
|
|
@@ -0,0 +1,50 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.HoeItem;
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.item.context.UseOnContext;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+import java.util.function.Predicate;
|
|
+
|
|
+public class Tillable extends Actionable {
|
|
+ private final Condition condition;
|
|
+
|
|
+ public Tillable(Condition condition, Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ this.condition = condition;
|
|
+ }
|
|
+
|
|
+ public Condition condition() {
|
|
+ return condition;
|
|
+ }
|
|
+
|
|
+ public enum Condition {
|
|
+ AIR_ABOVE(HoeItem::onlyIfAirAbove),
|
|
+ ALWAYS((useOnContext) -> true);
|
|
+
|
|
+ private final Predicate<UseOnContext> predicate;
|
|
+
|
|
+ Condition(Predicate<UseOnContext> predicate) {
|
|
+ this.predicate = predicate;
|
|
+ }
|
|
+
|
|
+ public Predicate<UseOnContext> predicate() {
|
|
+ return predicate;
|
|
+ }
|
|
+
|
|
+ private static final Map<String, Condition> BY_NAME = new HashMap<>();
|
|
+
|
|
+ static {
|
|
+ for (Condition condition : values()) {
|
|
+ BY_NAME.put(condition.name(), condition);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static Condition get(String name) {
|
|
+ return BY_NAME.get(name.toUpperCase(java.util.Locale.ROOT));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Waxable.java b/src/main/java/org/purpurmc/purpur/tool/Waxable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..64adb13b29b6757dcf227a55588da70ecabe083f
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Waxable.java
|
|
@@ -0,0 +1,12 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public class Waxable extends Actionable {
|
|
+ public Waxable(Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/purpurmc/purpur/tool/Weatherable.java b/src/main/java/org/purpurmc/purpur/tool/Weatherable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b7586f494528f30eb0da82420d3bcf5b83a1a902
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/purpurmc/purpur/tool/Weatherable.java
|
|
@@ -0,0 +1,12 @@
|
|
+package org.purpurmc.purpur.tool;
|
|
+
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+
|
|
+import java.util.Map;
|
|
+
|
|
+public class Weatherable extends Actionable {
|
|
+ public Weatherable(Block into, Map<Item, Double> drops) {
|
|
+ super(into, drops);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
|
index 07050c78a2eb6ce0c699101b38961b111d631a41..ee64ddb0da23ea1e54d0295324aca5b46a438111 100644
|
|
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
|
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
|
@@ -15,6 +15,7 @@ import net.minecraft.world.entity.ambient.AmbientCreature;
|
|
import net.minecraft.world.entity.animal.Animal;
|
|
import net.minecraft.world.entity.animal.Bee;
|
|
import net.minecraft.world.entity.animal.Sheep;
|
|
+import net.minecraft.world.entity.animal.Squid;
|
|
import net.minecraft.world.entity.animal.WaterAnimal;
|
|
import net.minecraft.world.entity.animal.horse.Llama;
|
|
import net.minecraft.world.entity.boss.EnderDragonPart;
|
|
@@ -169,7 +170,7 @@ public class ActivationRange
|
|
*/
|
|
public static void activateEntities(Level world)
|
|
{
|
|
- MinecraftTimings.entityActivationCheckTimer.startTiming();
|
|
+ //MinecraftTimings.entityActivationCheckTimer.startTiming(); // Purpur
|
|
final int miscActivationRange = world.spigotConfig.miscActivationRange;
|
|
final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
|
|
final int animalActivationRange = world.spigotConfig.animalActivationRange;
|
|
@@ -203,6 +204,7 @@ public class ActivationRange
|
|
continue;
|
|
}
|
|
|
|
+ if (!player.level.purpurConfig.idleTimeoutTickNearbyEntities && player.isAfk()) continue; // Purpur
|
|
// Paper start
|
|
int worldHeight = world.getHeight();
|
|
ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange );
|
|
@@ -243,7 +245,7 @@ public class ActivationRange
|
|
}
|
|
// Paper end
|
|
}
|
|
- MinecraftTimings.entityActivationCheckTimer.stopTiming();
|
|
+ //MinecraftTimings.entityActivationCheckTimer.stopTiming(); // Purpur
|
|
}
|
|
|
|
/**
|
|
@@ -396,6 +398,7 @@ public class ActivationRange
|
|
*/
|
|
public static boolean checkIfActive(Entity entity)
|
|
{
|
|
+ if (entity.level.purpurConfig.squidImmuneToEAR && entity instanceof Squid) return true; // Purpur
|
|
// Never safe to skip fireworks or entities not yet added to chunk
|
|
if ( entity instanceof FireworkRocketEntity ) {
|
|
return true;
|
|
diff --git a/src/main/java/org/spigotmc/TicksPerSecondCommand.java b/src/main/java/org/spigotmc/TicksPerSecondCommand.java
|
|
index bf970bf3356a914459c2d6db93537ce2d32c7e18..08221c7256f41ca511a4a61ffb2b979325aebee9 100644
|
|
--- a/src/main/java/org/spigotmc/TicksPerSecondCommand.java
|
|
+++ b/src/main/java/org/spigotmc/TicksPerSecondCommand.java
|
|
@@ -39,7 +39,7 @@ public class TicksPerSecondCommand extends Command
|
|
}
|
|
|
|
net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text();
|
|
- builder.append(net.kyori.adventure.text.Component.text("TPS from last 1m, 5m, 15m: ", net.kyori.adventure.text.format.NamedTextColor.GOLD));
|
|
+ builder.append(net.kyori.adventure.text.Component.text("TPS from last 5s, 1m, 5m, 15m: ", net.kyori.adventure.text.format.NamedTextColor.GOLD));
|
|
builder.append(net.kyori.adventure.text.Component.join(net.kyori.adventure.text.JoinConfiguration.commas(true), tpsAvg));
|
|
sender.sendMessage(builder.asComponent());
|
|
if (args.length > 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) {
|
|
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
index e9fa7faaa4451e36b3908cbcbbe0baf213abde96..a810bfd3b8d6bd4d8f2ef8797e4281ae4fe8a67f 100644
|
|
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
|
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
@@ -96,7 +96,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
|
|
|
private WatchdogThread(long timeoutTime, boolean restart)
|
|
{
|
|
- super( "Paper Watchdog Thread" );
|
|
+ super( "Watchdog Thread" ); // Purpur - use a generic name
|
|
this.timeoutTime = timeoutTime;
|
|
this.restart = restart;
|
|
earlyWarningEvery = Math.min(io.papermc.paper.configuration.GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime); // Paper
|
|
@@ -155,14 +155,14 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
|
if (isLongTimeout) {
|
|
// Paper end
|
|
log.log( Level.SEVERE, "------------------------------" );
|
|
- log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper
|
|
+ log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Purpur bug." ); // Paper // Purpur
|
|
log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" );
|
|
log.log( Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring" );
|
|
log.log( Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once" );
|
|
log.log( Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes" );
|
|
- log.log( Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues" );
|
|
+ log.log( Level.SEVERE, "If you are unsure or still think this is a Purpur bug, please report this to https://github.com/PurpurMC/Purpur/issues" ); // Purpur
|
|
log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" );
|
|
- log.log( Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion() );
|
|
+ log.log( Level.SEVERE, "Purpur version: " + Bukkit.getServer().getVersion() ); // Purpur
|
|
//
|
|
if ( net.minecraft.world.level.Level.lastPhysicsProblem != null )
|
|
{
|
|
@@ -185,12 +185,12 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
|
// Paper end
|
|
} else
|
|
{
|
|
- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---");
|
|
+ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // Purpur
|
|
log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
|
|
}
|
|
// Paper end - Different message for short timeout
|
|
log.log( Level.SEVERE, "------------------------------" );
|
|
- log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
|
|
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Purpur!):" ); // Paper // Purpur
|
|
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system
|
|
this.dumpTickingInfo(); // Paper - log detailed tick information
|
|
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
|
|
@@ -206,7 +206,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
|
|
WatchdogThread.dumpThread( thread, log );
|
|
}
|
|
} else {
|
|
- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---");
|
|
+ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH ---"); // Purpur
|
|
}
|
|
|
|
log.log( Level.SEVERE, "------------------------------" );
|
|
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
|
index 74ccc67e3c12dc5182602fb691ef3ddeb5b53280..52af11926a1f7973d70a1dae191d2e8138ec5c94 100644
|
|
--- a/src/main/resources/log4j2.xml
|
|
+++ b/src/main/resources/log4j2.xml
|
|
@@ -2,7 +2,16 @@
|
|
<Configuration status="WARN" packages="com.mojang.util" shutdownHook="disable">
|
|
<Appenders>
|
|
<Queue name="ServerGuiConsole">
|
|
- <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
|
|
+ <!-- Purpur start - copied from TerminalConsole -->
|
|
+ <PatternLayout>
|
|
+ <LoggerNamePatternSelector defaultPattern="%highlightGUIError{[%d{HH:mm:ss} %level]: [%logger] %paperMinecraftFormatting{%msg}%n%xEx{full}}">
|
|
+ <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
|
|
+ <!-- Disable prefix for various plugins that bypass the plugin logger -->
|
|
+ <PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
|
|
+ pattern="%highlightGUIError{[%d{HH:mm:ss} %level]: %paperMinecraftFormatting{%msg}%n%xEx{full}}" />
|
|
+ </LoggerNamePatternSelector>
|
|
+ </PatternLayout>
|
|
+ <!-- Purpur end -->
|
|
</Queue>
|
|
<TerminalConsole name="TerminalConsole">
|
|
<PatternLayout>
|
|
diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png
|
|
index a7d785f60c884ee4ee487cc364402d66c3dc2ecc..518591dd83289e041a16e2c2e7d7e7640d4b2e1b 100644
|
|
GIT binary patch
|
|
literal 9260
|
|
zcmWk!Wmptl7+qlLS~|W3b}1=IK|*5b5@|#_l<sa=8WvatrA11R22nr|NnMaG>0AUP
|
|
zC6t~Ie$33hbMG7HJ?G9mbDv4n)lnlSVI~2AK;#<g%KEog%-unRcYDr_v#bVzaIbZ>
|
|
z4OMQt9~6LaN1#s_Fh>FQQNXigV2~Fm&;xWcfNA!dm*#+<AE1{FEHmANcmS@U0MZHA
|
|
zq5{4N0?jhO^GKi`0eA)ic?N)M2w;>B)G7nz8bF6QFw6s>Er4EOK&%?bF$4;AfiW&%
|
|
zoCBC(2Ns!cigf^wAiz2NZqBnLz$Fx@R=#VuNdgNjK)EK+fB@F$fJ{?BsqoI$ED5Mo
|
|
z1rqE4Uti#zCSVl@%tLSX$$)HQ;Jq4<X9&m@1K;lhU82An{k!~!c!1|oK)CCj)k`zL
|
|
zCi=$D8*m8&O0@v%xH|}5fxyT;;EVX3!)zlU#TwAa0U})iEG^I@3Mdu=w$VVaJ5Znx
|
|
zSj7V}Wq?V_T{5lGcXw_}bc^WLZL$sUMFN;)2g<a7J|Vy?5x6baE%sXz_dr15Es$k$
|
|
zH?+hIv`PXo4nT=E5bXeTirx7asks}vHMw<i3qi8{cK*!=xjR3)FK^sK?uNFh?r<p-
|
|
z0by=;T_3+YjBO7Aw-6xO3Fs07Y>~ho`CY^a7vP8(5UvMwUftX}AK<&Iwsa{VUUgUh
|
|
z2@c>vB_LZ0XlDV+Z-B?IZf;?2Q{C)P0>ZVx9Q3a7hXhz;0*3DaKX~ub_6P=EnF2ok
|
|
zcR7!90-gbPNmi@e3BV~F2=oSCMBV|pg>WmCTgLr-01Q8169d>q-)SS&0%(`GBd$p2
|
|
zE<~Lo5bOy|!S8arrBXcyc!~mEJ_FLt@08Ob4a7eO9#jGH#Xy)lfU>@0<fH6e8I4kJ
|
|
z7MX4=67Dc|ivYi90GW5dXURJT1sd*9ACdqo3^ytTfc9&kQ65ONy_4K20id4_#Jm93
|
|
z>405w;9)s13%z-g3FI09k4gZC@SEGh21MSdMw@%zE`WMpeH{Z3F$F%s4K+W4gOWCo
|
|
zM2#hp$Mq4nbl;~?VJ4<NUuWsASJv?DFM7g4C);&i&IVdq%(iQjnL@27^^@Jri6QaR
|
|
zg+BD=^Ns1jwlsgwXL_n9+x5BrdaDaxQ#=io9-SQSHw7Gy_Sc0Q?ybyCzcPDry1qJF
|
|
z^cHD#ygM~9^trw`H{4lQRpR95;QQidQ@uT1O_dcTIT>luX8W7#1E`a&x!wY&zb88l
|
|
zN1p;u{w(({h5e>J1%Y6K8p;U6z`4mh7wrra#_v{h<9beb_Uu_ssLuklU5mIC>bM*t
|
|
zcnDp3!57GGcIWN~=GwhPk`|qj+4X29PCWJ%!mm8dv^)i!1KFLpM5tu4Zu)l4x1`+4
|
|
zuool8`RpVod-L>kH&(y$T!#xcuSSC206r^ApC6SB-*ez^5f|E=>CVr`YArBlJ*aH=
|
|
zv9&F3YdaKfe*MZ~i5LpP{mi>QT}i><&#tzbf_0y?pPchedeE97X-j8))~zv`+-R^c
|
|
zXW+`~MJv5ye2+%Kvb|-$zve#6Fq3j>e(Z$inlS;)z#xljVJ?019I=wkGZKCb>mY`{
|
|
zC8KIq#mXl@3vR}_b~1^fB_&=iJ$$n&3S*05tirE<8!l{ZZCzis{#|hXxvFTM`o}iR
|
|
zq}ASGAx!t@bKd6MjeH3iBEIB}%Yan>#e?6>$^PrcHJlA)z3Hwi9`j3t3g5m_#CTR2
|
|
zJWL`g(S4CEDe2|vYOlPr-diIV^oy)GsZk29-<yVVGHbejzQC8^`{V3vch}lU;GSV)
|
|
zWUEw7*#_T-ft2DurlUb6Bn#+45v>%}jHck!wdB5f==uk13XY9^5>Drj(Jal(oLc`8
|
|
z7D=J9cm#rVYK(uzdmUKaOiVbY*(WAOkmLNmxVRVy4%oluYjcE3ejCZtD>w+KsRMI;
|
|
z87X<1i$@!2V<|7#NM2W6ZEQ5}eehW7L)rSeirL`4wOgYhpON3G_`a-$K8jR7b1;WL
|
|
z!~?6_@enfHT1uN@893H{iZNi1|9834^3*)D`mGffWf%2a)wvAK&u>(jHANwD#zmPX
|
|
zb+%6usxsHvd2-@g4UX8e(%gfc=yCIMYT;LE&!w^Hm;hjMmbIls&Ko=!h)JjeBiK7j
|
|
zJg8+Wwf=llHxatzP37JYP2MYV!uiXB_G}g(g)9DJKdnb9^WJMK(TfEmW8+Fb{$ocI
|
|
zd<hygvdBllzJ0kNZASu33Un4&+r&~a)41eJ3!MhXcjbDoUN=&oi2g{dRXK=@i<6J9
|
|
zoiY7U_z35v8sReE0DX5hcZp14VF7*snLdQ?^x(}~L&LN5JQarEK;d~?atxJEiW~(l
|
|
z=6JJhIoF7@p*hml|C90`zF53MN}Wuxs)hXg-8cCHJ~aNGn^PR|nyGBlHZgx3X0ANi
|
|
z&liq2M=md)gb#cnx8QU4U}JO?YB^DzOO-gVH*WR)5<<<%N%zr+`n%CR-=wwxG4FQ0
|
|
zK{o06w4{YkYmAK#N*SLn;A&pufHQYdTJ-WNI0uh&x<siAgQimDUgoBAEI5rJAT9qX
|
|
zypcV8YFfqfX`y?AafaQ3n^mRoNoK1CdWSpGnvZW)XaS)q8lQq^6DYJ!n1^u~c_SM*
|
|
z%G`v6J|vk-FH!z<_2x&>_IJNIpT-3}l*&5tlx{G{r<aPm*zC(^CdA2!QQi|d5PB(L
|
|
zh@XxXDHU1rG=h}Ti~yF=QikEnK1jU~&|;}G9Knf;rYZQk+Uup_agUusdU0-U{!*aN
|
|
zR;9bgf3oK3O%cVg7x%pq#~u9dHXR}IKZ7)6*QDba1>8LcDpgZGm&_LCN*L6GgC7m*
|
|
z9Ckq~9^@3|Q4>gQQNDOZ2HyVd9pW<)EoE)RyU2C+kH**{1?C-Xq|NDRXjCBSZu!dW
|
|
zkWoaF%%=W+tXxD?vrJiW1>QdCcU?IwSAAVKXSF08Ri&so^yCRhU8OJJ-7;U}%kOXh
|
|
z;uh`2naxh}@bu)!tqCu)Xf)+K3I8Q5)ZcBvHOp43NX8R{ud%yohLd|fFsJPE=-WnM
|
|
zy9)c2e5+K_=dT7ym;4dn(y2MkPN*C+c_fH{ZfA<eqZ7iXr)ku8csPhEA+Akxi$KAg
|
|
z-h3<Y%z1#ZDX$X<a8;fk_lC)L>Wh`@@h_TYs$2*8y!HC~F*!SDduQi((Yakpx9^)x
|
|
zJx6lr@C*IFi6C32_c%iv6B`G$;M6Q37L;6Uui3x9r{WSzT7DwjzcfM?D1LbD#A$qd
|
|
zu4>LOEHC&2!$+8)#A2;xEg(p|Jdy(=RRcM>kUoYG*Qz;+&oyU511wbY(v-jRqxsns
|
|
z%#|?EAim6T>_zCj2<-iPVp$G)<1~m`qSX7m3`(@)$3!@_Il;X3RXlkq+00DbtS7gY
|
|
z>#I3K0|TFc5y9cN<3t=oP_kgwm69oEvtnYz&nRnvO7Njr5W~StAj9NIY?n)l*H$Iz
|
|
z2YURl{gS{bBrJXar(<X`b+qmA<Hz9ybx1QJee{cc;4t%vmR7QAMgxf6GEt6F?BBy1
|
|
z94Tj)f5Zt4qg#QtIdG@A*TWX=WKtRlsE@3m;%fJ$k(?qoOYs)gBAFQ111+v<*N4~V
|
|
zZ_FNe)#J^uTp*^u#n~J27r9bC44USao|ue*#!;>$8yfT4n#w=5%{AEV-@Grkiy0_J
|
|
z=#<qY)GB#66BSj7s#u*(E#Rl7$gh<(?x09V`bqa&5GwxOGG&RQbIkmkKb~RheM`F8
|
|
z(}WfHETX8@gZc}jzLm7NRwGg;Edmp-T8e7-fB0SGQS$kJC=*D`BS`u0znE`o9E9^m
|
|
z`mI&W3YjJr{hOw{#_fs?j;M6wbDl3X1c>2;?ULHsk`lZB<ca+B67*4=c=+)tQdJZv
|
|
z6pk9`nxIR=8QaMz^7MW8O*Gg3rCl71KFqr>!Pq?X9XYS_xO{hASO}zKeLGkV?N1%z
|
|
z>{sH;{En>vxtZU%K&AU5%Gwj}B-+Pkl=AbytOz{;6ed;B5v9!jh)%X6>$P18-6BW0
|
|
z9}ExHska~pvMsOcE?mMPyWj`s*pbbrIhx_ij%6Esl?wROHn#vuvN1k)+M*(8X8`A{
|
|
zS4o(sjn)kE#;i6MtveA?5(&g98!Mc<Q@HVtbelr4nO2#M57ZVI|LPs9)e=Z~n%sAc
|
|
zkUYOyc(Eq^j|gw+1Hq+<SGO#-LgS2RC|%bq8Ad#P3F?hy0EblKib{VN=wp|_NDX?V
|
|
z_KIK+$2K@#Gk;Y5f^~~iW>r!J5=}Qa@!L1e14aJEQmcLzXKl5nn2bAJ$tZtP$P8Z1
|
|
zZ>}d+7jWYR^n<^KOqhO^N==PYrD)O9EEFNs=im5ICPNsHVP!Zx`PfqbpT-5=sn650
|
|
zHSeafmr~){Zr!JcC1%$s2t+!}=}Ip+y3BYtETmoWiR}1P><_9ZZ0FT%gEZStrgdbp
|
|
zI0aw6jiD;PvU!g!GH_nZQDTX%5h%9pk4-fdm}0u~yzd;=Cni~sWY3r;O}XL;q&8-M
|
|
z47pHP%S*mY4oBx}PU^}#j%4iThs7lM7N=#@Fdn{XdiIW0L(=<T>C2~Fu=G5&Vq%Yr
|
|
zY&qWfrH35E^L#BnOgMwRc8B)xMX+GlbUbtr`nPrR=jS(Tb=kQ6o7eVqUZd`0fo9D@
|
|
z`vyo59#*A!saE#!$G7Cxfh7n>ZWX!0gkro``0W{b9=XxNGqu#u6^e-7Kk1lUvA@1|
|
|
zbiNNGZAv0UHvN5<U^P<r)g@-ialS1%)x3@yJ98Y|-=-l+Boj89EtWS_YS|uX*d!1h
|
|
zuEy_=OEANsq$z9D#HF5bZ{?yihjwlq>m{KO8QNS~$+u-B>s_5L`L`jBdrke`Xe{0N
|
|
zj*umKyN|_A>O3@@8y|M457h5tzNEqIDV$3|c%QOiW7;H(RNLM9W0najA-;HEwdD>u
|
|
zkW^fo^J0f?_u(^NWw(nT>JS~~smIbnC8QxFcuQkHNQUJyj;eYMTmvVN^O|RPDvczq
|
|
z>kslY@C$0YBmZR=Y@IEsCWJ9}K@HOu`c4n=$kr9!;n+9We=f8unlK5@c*o|I7(=zG
|
|
zS}lWvH_fp;i}4qt9>h_?QzN~ap@7nLh*7DJwfvLZA<V1CA-;}Ue~o+;dKMLq-ri8y
|
|
zikJ69`xKaRWgS_7`*rKX(ZaYWcag1=J-5B>S*_S4BW3*FwwCv)lD1Vg1t7><usf4m
|
|
zup`Lc+-g*3BF+4XfNOLe4Z%60c!!YYai||v?*gK{Zsre$>@!DLp5Su@bc$d;D_VX!
|
|
z57eDlJm5IkuTyc>aw+Wt#V5hkX-B+t+|!Fa@@x7=`8`3dZm3$aHF*y2VcD$(D>J3y
|
|
z%YwpO#gY%mqyin8q9uH?7OBC}Uv-@<Msy*~Nvry^su7=0x82-E)nk8ChDluiAzV&U
|
|
z2<tOh8_)LPqk+>)Hl0)Z2+y3x`{XluljPt{<4TU-m$XDvYU9HDGKSg&cT(@MVe23D
|
|
z<<w-7WUrsFSZxtTfqSUs&=EIdlxvhm#YJxmhO0_;`8k=7skM9}9DkoBCfVpFC0Ohf
|
|
z%}`!2CA@!leO$E-;~Y_b`cgTypBF@7iEd+J^tUv^tjfq6f2i#0!U;Q!z8b*8of34=
|
|
zl@qQBGh&YiI|>wCdy-3tTl}j4kzaCmrni9<xi<;Vr$W&`EbLm?-R1s>Diizn#a{@0
|
|
zu;<>ZBofe$`#ML)Z*Aj8mbPS3-(VgSdF3LA8LJgBPj{3IaB~uLBuylK<S`<40r_-M
|
|
zoAmqT`YvtUUk(<9&TZp3#LaaUTWN+^#exVp-Py|>O*>ev*i|tuS(Wff3`Ogj^{GsE
|
|
zb-&NcY_)d|4#vv+AW}skSWrN{p$KOp?oug)BPoM*O}*h=wrr-PGYCt{qwP-&I!~hn
|
|
z&+*{EI5^09slk8i%kFcTjCcO6Oc}M9iiS}cuLLw0fyMPfjZ_M`;HWDHP^ke=f*5^!
|
|
z(0!2p!N9nZ8J+AP5sfunmbw64l@2)3@53_`Q@g&4FYKeDLbz2F-Pl@Er#INAtM()n
|
|
z<;MFT;osC<bQdAh7ukANy6Uo{LpNoJ5JR#ZS?MQZM$7>}L2=Y4P1?cm)V{7nauSgh
|
|
zx4)k@#lLT}(uM?{Z&NvzT8pnJk@-MDvv4wOKsY+l9S8OdOw}cex#$t*B6ppAEloQy
|
|
z8u`9L`Ky$*$*G}A=)h6BjVn=_YuPie5epXe0vLKZP=Pxps%a&uGrdg4y#R{YobnDn
|
|
zWlMl5J;9|5ev`f`BO3Uh(yuK%@i^`+fAimq+_>*)z(;wrFdXn2nVJw1y_>PcV%p-)
|
|
zt#ZCU`}~DIE5y1BiI$qSd5XIebq(uRB-FnL#^x=bDHO-ls3({4R}-PgePOPH8ggGN
|
|
z^EAdT|N11qATWCZQ}ZY#=hum&;_xeyp*|O{VN?%jM$?&+DK=8LzLP4?U!YQ_JT0`M
|
|
z`m!W@TCB5mlr9&jJ{^cX4Ye=uf(7g!BDG1LQfZM#P5u-^$L1K~rOMk<H0023v|Tg!
|
|
z@<`0{o=Z{LBf>7oWI24W>ZJVoZ8!*NX>jC%2pgw@7$v*amGL8JnA`*TjN)y=JPix$
|
|
zHom^xjfr~ZFvhO?xbJHg&jfEZgboIqN@pk*%G-#&cX5DM>_>dMo{P<(#6bOBlgEyG
|
|
zzi7pMrVsz<*TT+n%Xo*+rUV<N^pGwpI6#DG$bDv{0i5)d@1mV;xmg}Wb_OS@^o9%P
|
|
zF#V0~d?t@Ez~S$v#O2LL)9K+Q5V<4HkHp@q@knGeChs~ZMNcp|{!@^b#M?o5^kyL^
|
|
zMbPEA3LRg=<{=5)D@heWx381fV@moa582tdi<JjliS(nCwtPwb(v(Engz{oD@CcvF
|
|
zzd~$BNPA|mS2m$u|0%)V15A+WyQcy7y)R#^UDlG!`;Y}^5uP{JDnW?6B0HvlM~UCq
|
|
z^av2YiIwqT7Y_Q@*IN|+7mdeRzX${b#y+sKNTa`!M}{7Wdxu_0l|Ok2RJHxgZb{;y
|
|
zSn+B7HpQh+`q7w*lcY?}HhJ_+aBfuLcN;Q|-fHZEuh@~+keZD+8(s01NP<A_Pz6>N
|
|
ztJqe<9EEg`YU)i$PQ+gn%oA5sG>b*6QUjZVf+Ju4QKWr32^B^>#t1pQ*dNT#SxuC8
|
|
zGNJ9mH6R-92O+m#!DECXMrB|3+|cZ$?^qoag;w8-vr)Z5hx@2MVoazOwqdLo2-lu(
|
|
ziwF<xL&6n-Lw^N|f3DMxm<_)@Rd-usrtOFEhAX5<Uqh8=vZhDSEcrKIX29ctvIybD
|
|
zk8_USJ*Q4~re>(a3SNH{+U_=d6KFp}-N^eInrfTZ{p%Yc;K{NXO^Sl0qy(%)%sUXL
|
|
zA6Ic}&$4}O+ulD-?|#%otX@aO$zD_RW;|WXeL`u5;iz`2Ja937zabkT*wwyB&2HIU
|
|
z%>tpNcvDWCsG=IjRr(V|Z7N`9^tphbhiC}fIrVcS>=>)uMStm;g&z-HK{fFz2ud&X
|
|
zKo;y87PhB3IZGZ&D%bJ2dZ<-Z7I$fCrPWl6O0ir})>f~+rM8k@!Q(EpWCD9f!k>me
|
|
zRhs@Q3_eZ!{s3QHUMaN0uS<rI{r;Qp%hmh)`<m<F-c_OKUDcphTas*?ZC+jIOHR&7
|
|
z=5Y_AhBB%7F7^Itr>hhn$<I{W5YxK8Eq=;a0}Jxgt{imOB@yy@CUt3IuCdj!9h?un
|
|
zN4A}f(~$LvrbNyHMqSzYcIG)*5EV(scW-Ek5Zl#i_wj!wga3K!Juv_kS1E>y!N+)a
|
|
z6F#K#O21}#Wz2NGar1LUq3S|3=0~&VTjxVeqcysO1QIGwgglMcjXc3^9R9i556MW!
|
|
zA%G1+Y)mPX16<vM|IO3}+7XE&8+HWuH=itySZvI)L1tD8NSy32dia>RcVB#B?R~Xl
|
|
zIeR6G9EBEo<!NPj`1l_*1iQMLG>U+Sk|=Q=4gQ<j4<3!sVEnV_N2#qa$LMWH+-Ra`
|
|
zE{KmwwH#KDkB2w<NET$K>dT~j9ecG~G45m|E+IkhfuBxT`M%^wZkNkXpL0AjS!$%u
|
|
zg-={lr6QW@$p<#-f}T{y|0rAEpY~$9`Ffd=BP+`1#;?ge=@mLGkdmI~u?Dai2i+}>
|
|
zr-d}7M&xTHsK7@<3$tPd^Yg3fLzxDa!oOJ_N!hGt&$}5!9&TOIuzhIP>)^&CM1CIF
|
|
zY>A-293)fzq2lbB(1wKk=>#3=G1eTT&8(iBSJab=V@AGnDSwOhxKg{m8sa~TR||^x
|
|
zH?*-f-l|Zq;GkYEt~~O`56i(Z6gi`*^TxMZuc-f=NM<b?&atrP|2+?p`a~XaL3?_v
|
|
zis~@WSUcV;T#VWjWjy+4jE`|*KuFWU*vs)CvUsO+SBkchc5D3%U8KBf)6Vc;<WwfM
|
|
z7Fy4B1nXku#^XBwDJk9<N9Fr#sAp04Mx*Q>lvry~%u2a>kz*@R^tJ@{7%-A@wfa)Z
|
|
z0~dL|Z*tTjEX7ED(M%2gZ|Zi_L4TWr%=BR=y4%;?-COo)8t(xaW)#f?Uhc9^d-1?K
|
|
z=UDW-cu<en^c|7YEA({$pTeUd86t0Lz1+?=lZO1%7LXFlksTzVWhH&*Oe%8Zxk(VS
|
|
z(7LDUjeg`?*4VZ*llKa~3TUeTF5<7e?ceps(n^0fhev>8J#%~t1$>T7o{K5NXC7Z^
|
|
z;*V>fuSp>3%L~xxUi%mG*w7fJS9FTtSX8!B_=C>_+{-q^3-7Yf_esz?&oN2)zkaPH
|
|
z=7a>>$VgQh6LDY?h_Px)L~(27=d%@0UX!M7G~G|aMRJuU!|xkIYM`l6jEi407=MtW
|
|
z4YA)qqP8SmCj;2g*%y+BObhJICRimEtC(yhX|B>fl9#O|OsP0h{YfJM(l{DjCSf;_
|
|
zp3jZ#S5YfJ*b;<R6mYOg2I?kRF;LN1+TK7+`pf>u0&zd}UOl+UMYMCH3kOBnUGziK
|
|
zo__#J<(BObj|aew5%LI%9K>!}5UVhW*2b`jvkYQXN*iuj#{{Oatl}s!dyhU&jWEAS
|
|
zdKj4YrSTiJ_b6i{@5nk1r@W#B=YNdtri~y>StL>N%B6Gj6O_fwN*T-2>1Q1KuQAXE
|
|
zF|^na>CtL$iCMfa_^Bio0%XY3)>F9Uqzf-cky+Lb_H)#4=L+?00yMptx;^o9v}fkd
|
|
zWN%~1Sj%)#ggvPi=IpG67#q)qrgFi21qV^a=2vb1s|%}692RR2)zei^W-5JD7Y!4^
|
|
zKfyt1dP(!slOA11Of$_Q<M(4m-xsoF{41>uYE4W{Uu+DO027{)!<nRB+N?VnRnnx#
|
|
zy^B5g{ySEwgMklX&zT92x9SjzIneNuB2gJOMJthYmAQreiVrQT!%NQ^lBcyRR^NG6
|
|
zJ%pn-fN{KiYSi6-uMpnG0ugSpzPxf%T(Wa2M($NIp_OnjvKOe~)nh$5O{EGSLt1Gn
|
|
zM*j&=mv6vnH=K{@JOu`Su-l>7+#Y;Ih{M{`=>H7?QlKD!&7BO7X`Q1_b071zS3lNp
|
|
z@&Iv~QHe@3e;q{$b7pE51fo%ee_qC4)CR?kSh?lvkF}D2UuE>>^dhlmktVcN14leO
|
|
zB6JLU^U}S?*F=`1q~sTqli@HC^RzgoXWg7-*R1R~2xL-K^`XjV%3c-JMf~;^ILFoK
|
|
z%NIwJAU)}8T@nI{XRp;rWaD7cuvC1dj!R2oZ(K-<6)PFEe1C`3{NVSlqPF<~-&+*O
|
|
z8>_z7E5JY^sY(1!jgGp<)OE9!*nx9!RX3U}7tvu5m2XXS(V8#x+t;nz?=xkI(DDsz
|
|
z;3foX=tMqziX%?kztoj_MYP1$@9}OUi0@Z>26`$9-KBzz0d+H_Z`PU9?gSA=7?H}0
|
|
z{c+|*bet;11)bfWS<)JER^z_5PKJN$@9unBRX+W*oX^32ly<xVw{^8wg!tS@5$mOA
|
|
zvV-*+V_0#NIvJfmlhan*-J;}~VIOEsd}aFrm(Zij%h4&)Q}r_gg_CQ0C$l#So^7p>
|
|
zKo%s>e!Q4Gb1DeiV*R&fzDz|t8*WBSo1ove3somHjybczT^qp^D^Tpx97n^9WuuqI
|
|
zTCFUlzMc<9(@Ng!L9ebpnk>0!&~jV<V^T|KFe>#<spUg}5Y$_N>PR~Lz8p-9KECK6
|
|
z;7<rs=qt;RJT@>0`vR#D6@#_MoD+$Wl*AAbY|FVo2J6vOlP={WYAqT#$Q!S^VW9|!
|
|
zl45qcN$prx36zxggrb_z0Ri=i%$I(SJ6jHx(uR<;b(=zb>GDa-cZTt=EWQ$^+AKKp
|
|
z!qtgInkt}{k=PN-erHn(dHX?T{g44@;}a%oYTE7q*K<=mU`H~sCkX#6pyH?M+0W>N
|
|
zUr<(Whv)VbXit9iJY4V&;_6xGNVK9d_P*yRzW_7PgbR$4WPdD<VSusjLlI()Q82sY
|
|
z+kMUXO0rqfgfSoPD0eyWlcPt4ltYz_5Af6<BEHeWh}&>P<bwXH=XFQhN1kLWK^`vl
|
|
zvxIFVF&S7E-eW6|LBSSrFcLP*e;W(Rwc$TlH>8K}{V;@u;E04A#lw1fxY9qPYX%78
|
|
z<E6hN;j7lL0qtd*NWA&tJf%UHY`A)%NK96vr*%^?g-zB@SE_dfYL5oe%)}Y=P|Tm(
|
|
z47xne{xGmP7m>?2?m&UC)5IREFbfd%r<*8}KZbL-)2J(s!Ni>DER#ah4jLH0*2GPn
|
|
zFP?pTI`d2+=5;~B0pYU=P03Hk<$aGh?7&|CDV>N4L&S*{xjJ6{dn!jPj@;!U64ZH$
|
|
zxcv;Jen{NFvhysj-qs<-RN>Q}{r7$Q(|cMV#FVvG1ygSsC^0)`=DwB(4Z7$pIxu3h
|
|
zGr}-!!|aUp$DUaVeC9=coIL@I)g|Fm7a7u6JRy|q_3SIEsEkSTn?R2}SIm-}g0D#x
|
|
zbFUgC%_08XTvJ@!3#F5}f?b~Rz6CpeASaHdWnNs?a*HD&&M#AO;^>?RDV?gRN(Q&_
|
|
zi^f)H(H|$Fg#yiJBfM<kXbR+8_qS-OW-~?AcS^3}(a>*7wTkz_6un^+@bW^qdO0rV
|
|
z#80Zot!buo$fS$UtrjI&1`2u(g>;~*Z@I;ZrS@=@)pCyAT-4)ZtWEg^;2QgIBuYCv
|
|
zCOT@SpdJ=?l}vNgolDevUl8e7VBri&69qGA<l<=PFtgy72x<!`D%&pU{QUIO<p$OA
|
|
zjF-bg6OyK;s8eZg@0b9$JowZZYM%*}>FHzdn>vz;Z2PIH&X0IdJxq5&G|lD{8;FN)
|
|
zyoId^NoH`da-U0H$$EV_2u9QwI2NQ6xr#xsr4!7>PZTS-+^Ser28(<ufWAbzK^iL}
|
|
zs)K+^CDFW1{QfC7@xj|5V|KHulJ#Aef4_lBRp1=q2?oTXqKfE0ULI7834iAL955Do
|
|
zSy~-pe;|nZurhRdl$28Y`nt@lo;`yDk8TFLI)hSu#B=h*&Ttu}xf-}FJI8wOj2n%E
|
|
zu;MZ@I;|)66I4G&8E?FnLgR&+MXh9y*Pbe>?H>pkyr(t|p)Hl*Rcc?7hPzry4)mHP
|
|
zWLX9>Jcr0gs2&(aNg9b2@BIl*r~2X;kn=eI%P577nIX=auf8fXGcC+->CfU?wHoJM
|
|
z@{%ht0!KC%-tamvUWKvNx!08PvLF(w-EK-u?Lgd+WfX$LOYI=5t00MKD|Q)#@9mL5
|
|
zWZQ?eQkgCCqwANoAm&<MrorQa@YB;5=z7i<ld%Ofgz_!qgzU};NJx4Xbn2tO*M2c9
|
|
z9d?~K8;6b~@JV2`9HJOh8+HrL{3(yRSote{jC^a9g67~g%|9M3Il5>K4|fw*k7ipV
|
|
zB1v2pR!seE-oV-2@pAb2ne;%k;&0+LB7z16@)VH1MO9)MHU9kSp)dCNVB91j?4mmO
|
|
zv3NP2%#IeX=+W7Ni#8IjoTpc(!3<Z^&wgraQ+Ie66!#M2gbtQ34Uz6O<}jO*XaM8g
|
|
z6C;4cj!mOQ|J||_)bfCXJ$~f0tTvCt3sM@w#~JVwVq5y+y(mvHNdc`$`Um)5h0U2x
|
|
z)Mu{QO70#@WOQ8uNn4C0bVBT<?z(@Eo(2Ew(3$a-w+Ab}7*^JBmw<FTZ2pMaB)izF
|
|
zgR1AVZ@axD<4}7I2AeCsuJwt>T*dt7!EX8Vl<I;?FO50vnpx1YTJ1jCX)(4)Nj{N%
|
|
zQ849yy&@&j=J^g(>AzQq6uvFcs%W{$71OuAvW8Wpseg+*PVYLv?WMN=C|H@$;E<?2
|
|
zy{V$HT%SB%FYRPvzJD%)C7_fo`ov>_S5fVp$L;GyupU7jZ$kfvC58X?uLqEZijH!v
|
|
HqBZh=dic2S
|
|
|
|
literal 14310
|
|
zcmXY21yoy2uugDycPmm{N^yd_Q`}vP7YOd|PH`>8wLo!q*HRn`6f5rV?*HD)IX5{c
|
|
zxpy-=`|Zxo_svGBD$Agwkf4A-AaprdNp;|J<i86E0eG+0smTL@K32;~ifMY~oOB|4
|
|
zk?uFYy)FKcYT5sENxvF@$`?h3Gna1CI2cACKi}}1s=R8n*6JtRqsoMO0sL2S)G+$+
|
|
zwe03HtTTAKQV3~*0umofy#$i3BMyU+*?2sQemV<#a`oxkgdnZ$9;;DP_JdGD)$D|g
|
|
z72WX!|AFv<a0Dg>21vifLD%hMrT$lZeEex|7Xe0mqFAZArC{!qp)zJmbab@utqA`6
|
|
z61Z~|e!k$IbXNT?PvGuuzT7G514$8e!}lsR>%nURMm+~pde``@(!O=ISt0%B93;Ez
|
|
za-qRi4n0Q>zQ2#2^_y08QOl3jT*!Ir5@<8VrFx(6f<g#SP`8lK{xiWyOY4iZsp&Q=
|
|
zXovo!U=uNC1H)#a$L2hAG8ej#)@9UGQ&6z=D~(y(s8W?tT|q%%8g*tL5nUNV!1q4w
|
|
zeRWIAtsLkhESBPm*d~aq3v(ubbDuLjF`B-r-!^pxgk*TUXm=xJ*9`spkqyKL)-Cv^
|
|
z`8^ouoG~5&!3GjluYK_%ock-jO#u4LGOV+*m*_h@Lq1GH9dzMzWsmFt#}(Drl)XK(
|
|
zQiGay@j})8ip7q%+i3<AjGRCgj#PO|aSsm<DLJ`OLl8{|?=M!!l~%zKa*t`&^@{+b
|
|
z3(PVk#;sg9VGt*5X-SID-`6%{oo&Lsy0(^ma@J;{-0#LaIF4h5uxFbTu;_AZeEeLs
|
|
zLNk?{_3GEk+dJpSfS`FNkk)Ri=cNe*gNKjOkdHECB<K1b0}&JI#|4F|&#p1Q8&_sP
|
|
zF81!EW~%rmS*+Hr%&L%@%vdOyIkP!advkMuj+YY{$}eB4ZeVEmq6%0Fi^~&!f#qz&
|
|
zJ@eDL?}-cxD~K=N-b8XLb@*e}&dh95SWAmR(T6GNU!Gc3jfRzyrk2|RAnh;T1&tjU
|
|
z9b3)gDcKL5>9sP|H8ttjftN;wrX>jP4BcG1;MfU5x^L`zc0<A7b=d3bZvNqdokcd=
|
|
z*`V@M<m)S)O|$Lckz9XIk8U5OI(gk5oT@VpBOlnp10*i!lOX*;rPFtVl26td2FD7(
|
|
z&}(vX@)LNV_2Wu-P)Y!t^0R+1v1J4jYbzOp^9PpQXAeSYb0Ov2F&XP}7~VBqaWekX
|
|
z9(ZGr6got2TDP{XzJaszsGi=;YTxK~m#0z8N$BdPYc#h2D+D)@qww1|Sv@18E&%S1
|
|
zMgB!+=r6{z7co;mI(G=QBqd_fW(tt3{~4}eA9-}tb7H#-WUZAGk)<m7@5rJix@9k6
|
|
zz)xP&x^z%-BV&lb5fH=u(TqJ&@K!l7ppH~h5{+oTtu^w$ZGf#6y1NkSiVy5XmW?dd
|
|
zd@r@QxagUdnyLv!UsjL5OG2c-C$yp~BDS9mA2+dNA|gzMH2tuaC{F6%&LkqBjvNZS
|
|
zx}7I6TcoCPbw|)13o)T1FA9Q*M7W|N(}T;SHJcOuiOKV9dXT%kDH;-jKt3ghsRp13
|
|
z2SAb2Cjdnu3JjR)R+<OKwsEsh6@vbpD9GF>9u!bDBt#+l<W({$p3w2~%!OIy6U20i
|
|
zJDW%;$K4kscCQvjq=_S}SPO`WT$nRmuF%zqwdW2KSC_tfl)dh|3<aiMZF?RD>l=7@
|
|
zB;}A$BKgu}V?#qfHvm`~pt%wG2y{MOc%B!8I`p<X@<5o)EfV*g9pvGozhhJ)@Rrg_
|
|
zk51{HFj6-V7ubRs#Q?Qiq#}IDGT%r=g~%fw!jf<iMreD|VsUT6?cym+9ST)e->|pc
|
|
zO#?sq!Zd&j8UPmvY4RQnfo>!6{a}GFV!}g@qu<3Wu$07X(O`vikNW$~q!ngF23Ls2
|
|
z53p8js<-B_Qd?xX6rtq43Mdz(jOg2QXx#Wng_9^1^^~KqFNq{Kvb@Ap9}bf&xFA-C
|
|
z5+#cQ`#v$A=kd0O=agATcleBaxXf_(dnqbQz|cL9R&&Ni1omTs+6~YApmk)MCghxj
|
|
z1}mq&IU>1nEiF=q=PI`%jQbyRd=hVI83Sm{E-4uTc#w;NN<X9bHp)yNW*4(sF}kmh
|
|
zh|EV-<*{ALez=}IMFkaL#ki3?K7IY;3li<MO{AjE7$3B>wEW)C(C`xvWzY_%`_MmO
|
|
zD&g-sEaE)}6(&g)y-N&rNy;5@+{M`}!{60Y8wMgF5;HmO#B~hG`W$;7xLG*yF((rq
|
|
zxP6I#r#o`B3FppK{v(q1!C+YLFSfySDcHyoW!}EfzuCB1B|C5+oP}dt<N4UgYmmkJ
|
|
zu=mwXUDv!GNF`OyBy>ocnwkcNy1EZ6#5JX4=ePl&cu~0tMnt&79+I4%PaK>VqF<F{
|
|
zFZ1;DE;)Jdj`>x;r!Qd<o|T&8I*^GYG3A?bWY{3dQ+Z7>NmnxlEqdU-QR%Nmu{aWP
|
|
zJxwXv<K&Xd7ngEjj!ll3ELma&5vjOv@%HH>t5fFTCOV<Iwh1*<Rh|6j2Oq!>gB)Zq
|
|
z%H0U=9q7Y0lu&1kc4zYT3*lHA@XJfoK>3WFM&WWf2u6^+wCm8##D$x@Gkw+t^HoO(
|
|
z4pxDRqg;$5S=t^k22H5^V3V0Qfy%Ogl8I%LD$52=7)J>Ki9Ej1HyEi_u<Ky8nQV9t
|
|
z1(){P4e~c8WP(r`0t1nf8q6LW8?yt24Rqh1@Is!PaJEIFD0kufqd8?cxNzdq(}kLT
|
|
zuop#`KYTG+6f^N-J(U@l5n-7oK}@pcl&sDW<4Hw*&Gd9P;1Y_IT4yLQ@eOgPM!4t?
|
|
zv2K&6a4V+_7*?@1QlSXCBYfZX-mqFtqBL0{O<pcmuX>jELlz8$-+?cdD1Zxi02kW0
|
|
zaY=caFq4~s^R?zxcc3Z0X|az}Aww<{P$>6rk+5Di5J7$kWor0{Q&>+DWSBH^Gf`SP
|
|
zT{4}IOFh-hB7xwBdewq%de)q6QvxorV(()2>@j8i!kj)=<pXWeWZ(!&WCXYnJ(9dA
|
|
zhX`T@<E0GYl1247;Ses8Miyue;JI-q&Ziv;WJDEig*+%Pa5cvlHZ{GHH0xb?Za#Zj
|
|
zVU&wK|K~8kUt<~Db=5<o2Z49_J$0WXc?NAAAl-7|OG^gH)b<J|<u8%?EwB%)SZL!}
|
|
zUj0&76rIGg=2|6pHzsPHh<NR^BYz(lxO`Such&!htsiA@!<wr9@s7Su8ZD@iut7|I
|
|
zI;8w)-X-=+;jK00=?KXuIO+95T@)%$Wd_5`CFrfQG3`t;AOox!C|vLH%Z+1hPdPk&
|
|
zBWq?I+*jBk#h=lqY`AA}EqhHKiT}BNz#565iu9yu`-sqxhg6aq6<8I3Hwud(i>^hN
|
|
zl_N{$9xTHHA;V&Zx#tX&1pOO;<Ro@U45P!qAo?AASuYG*AYY&Ooi%x#%b)CFP0)D$
|
|
zs39{c0pHwy6+br@o&oE(5r`yfX10?(Fffn|$zj$3rqwf1kKN%NjPOs6Ko+jeK8t8t
|
|
zZx!Xg7{0F}|D=485U;R4V#!FyH#7-I#>v^NiOP#_UK@J;;lp+OOh<G`dG#Z+jD8-`
|
|
zuGy;l*h58S+P=TP-=A_HB{FdD&mXP-E`%KevQ3P5GJf@<`6K!%xGPSBBQ=b8+by`z
|
|
z5Ob1euIOf~IG*wn$@apA1`c${!tLpwm<=yl7WzaNXRmESFcVW!G&3_Qe|`w<$wfvK
|
|
zzN_sx8JSxzJ4}(5eP0U(4k99HewGgYSab}S5%pb|_xmtAY}LP&5^m0L==sR9mZtl~
|
|
zApb2RPCSW&4QJ<2P7&_<g<QMyBMXgB6I)wIw7y3nITujN=$q|AV1wD;p;U!Zst(=~
|
|
zl#i;Ou@6a!5pxX{btAw^GwAAQX}w2PQN9Vh!wA9sO61}kN_y2cdFQ3VN5nv-%$AZz
|
|
z`<&Gn`0Ycs5ePb+?E+(#J!nCW5szhQ6yKMr>OOO2mlMdxM;Qv-mWG+^vzox|8t`w|
|
|
z=gPlM3)y6G*hfV1WwuMe>bO-vP9g`h5BqgO9x{ROBD;aPl>XDmvt(3PUxt|4RFRpK
|
|
z5OEtRz{(Oa_W_!Z4XHf#h;Z-~71XM7wlF*L!-#h_Uy2tGuy-rAZ)4{qE~feNkp}qf
|
|
zgvBtLkFPI~I7<hoG?bkw)mOVF*%;)lK%ly{u|$|3Iw7J>%C=OHZfPZz$j>L9)rb;l
|
|
z@J^dxncy52;wmHg=wC3|Xn6jPYCR7<T~^e94N=B~zcTRf_@?^gFT)p?AIrBJa9;*Z
|
|
z(-DaG;r7--)hh<3{cpLe^qNuB)YNR8oQ4I@J3<0pj*XoKa(lZv_}#R?oc0q0pf@;Y
|
|
z@|$1S>xc}~D0wNjoYxmoRh_zh=6@8coM1UQIa_z*1)cZPw4v40qoZQp-uy#DLv=oP
|
|
zX9b3vzFA2r8}|_AO8W1(OMG__0{1AUD&Z%&7-(>s+Z-X6Sv}G5QguIbZ3mYa--?09
|
|
z;wNw?n=yAag4%m#w$$-YZ{(ZJUcwHfzu&!gykNjG)e}!=q8xy2_KS=ULsQwv45NK!
|
|
zVqqD8#S{vRjg4(Q6HM_F&tihNIQ<ph9XS{sw-<&Fv1e0-e57d}%5^<oCKT-=3{4`y
|
|
z64WO2DNM@9h#+<9z$P>ns<%DVjE$cv33ET>Dvc^#{z&#u&&9RgXO?ZLuebczKv#;!
|
|
zCS|2lIa37Bp#3RWj0$V3=I2>o40{(J^LD|EUH?!2;Z&HS*>7*V%{v1)wHaUP85mcX
|
|
z%q!K}Ntr*IzJD%++btJ;VQO*OjJL1t{GvR3cy@OC-~pe^bV?N`z0QKCr?Tom)4u%A
|
|
z3mi2k&eIgh0^rGI<D!3ppe*5I#u>#Di+&3lrsy-r+}zwBkDQtswtPbkj!Y^l`{f!#
|
|
zLseC0M;DiifDa!({-G4{W$Wxsgv*(NX%HMyXhArVwY105dUHg?+=@6Sy8n@slS76x
|
|
zU7%PI8ToKm#qahfR;7kn#|t@9y(0EkooWBDqA1(mpO)>BBz))giBi8xVHlj#dR9U8
|
|
zRo%`iBd<rib_r~m5n7z6NZ2m_7bsF#7pV!dC-}k@FFQM%1={&4v20&BgTVBJ*mWm<
|
|
zN23p!P@Cn5GW?{dLlUasjp@zUdq11tADUqVjY5iK4}(SR8OYv}JKyMhaynV&(oHy!
|
|
z@}!@UDNpAMBUmXC#>lj8%_tRn^qa%T>{nsLLwTNld&WHLyfbPzv2W62m6q=Nsdxnk
|
|
z#{P==5!Lidx3bcr_qlUl%BX!xjywA?jv>FU^mJDa0<zrP{CvIlmDTgZbbz$Kf7j-e
|
|
z+s*)TH@To{E4<{VPzP()4KKg`(U-QB{S9iS(ZEBSCBv-}8Az22>zQT9Kw8RRHq>7B
|
|
zb~DXw0(oqBrOQunsm2ghWV2i1VmN{F?)U;0%*j{FEUxazAJ3)KSWomuhklkDi<zIX
|
|
z9Be*3Rk+zpa@IW5+&kJBa)4JboSX7tEK}FzcS!}-&YS}K;LWnJigX2xl$)Dd&(uEq
|
|
z2&;t*>?5h*MTLDS5ma_Nk1sNZYzZ#$maGRyiXBzjG@(G__fuyBl(^A>s&{jF+J%5|
|
|
zv#7nD1XK806#_U_4#N2ANAxznk%;U$Y$z#{K*O07mADqx6LjACqwP<`HFV#C6Q*wx
|
|
z8JVP_qGF}V7B?^8)f*2F5AON7v$L~Kr?2}oPai_kG!_6MI(U`LS~+Mo*CSyrw>pPE
|
|
zllqxy<P@nA`e}=V#zMNQ)dt#A_#9nX(;m&YwQS&qp4EYe)+anT0N?#z4yCW}V|?08
|
|
zifKMLf9AwZ0;{@(dKX_&!2;%Qz^R*2)AC8R?qpzy$<pP+$qAVHfi2I$)_zDMbobk>
|
|
z^&rnDn4XA@AUY7~`1lwTCrm8KlVRqX&!kZFH&;i9@=R}UDxNSh*)Iq2U+#9}@ag1t
|
|
z%KUOEw0DXT)>hQoLTprY^z=BC=8NAyi3pZWT7A`?;rI<3%65Nqb93%pJ=!+dNtB>W
|
|
z7f3O-e-S7ZBgBntcyt~wOG_p$AU2zlGH8=%TEm+z8kLYReEMTkIo#2YiA=iKWrH);
|
|
zS%uT3xAyyY=!U)0Evpgx{{38MPR2nN<3913M<0O#YCO=TSt^4IzV3^D%2zC>t_OO}
|
|
z_h~AVOk+IIi$Ov;-g93a4j@WaekCC#HFm2_Vu9s)8-GbYtr{LgrxnSIN^PW9)!jYX
|
|
z?%-yssA~&R3F)C)wj5i|@!atCx?Qy%P1QEGSZm;iUNai`-F(8a%y+_a>CMzx$XEKx
|
|
z>sW|JbN36s+Y{4SZsrspH%UH=+Q6J<CRu^N5ZmJ?1SFBed~3QFJ^YZkw`cKu=Gje~
|
|
z(AOuPPZ=<sC*1n>`c&_-JLGL&5|$XUA1vFOC+rgoc&xT{dFT&pMaEBKwy<F(IR*1~
|
|
z?7VnM3^J({7}U8XhZU}UO%g=gp%x-^baW>D;plX0>2nla;jTlQ{!fn2M=Ak*=K*g%
|
|
zBm0-$ly1~}CT-5gv){jex9)7&b8u!a+vYHXU>=NF2>g3+_rN{(LUMGwRWKk49sS$v
|
|
zazyX8zZ1hwZ|U*5{fK@i@hRl*U%Q2cg+!iIfb)6W%S5F{91qinEZE%~4Gl>rBw9S<
|
|
zMP5$exl1j<!yq;^s?0O{SV9tFS$-AUOcp7)+G5dPiVUQ^Ww8PXV{7{=`gm9@8FCNX
|
|
zX_OEhjnV-)z(ORF{aBkd6c3lsC~u`q=_`fnK_#j=XrK1X(ZSkpmPYHd7I*HDiMhJ+
|
|
zHIDWeGWW+^<~MG0#<jQY2+ASuX`zsF-vdE^!Gu+Zp<4eN=9BfGgv?r1R99lY{AzZ+
|
|
zC?kMRSpc81|I}uA<fodVkCEdG<C~$y9UXnaiXqPL%A%Nbo#Z%Ca7ISrZgh?${VPnG
|
|
zl$10u;C)>E<KN49z-H}%ot>Syt}d~jo?hf`z^32b!}UGtJH+w9(0U<yHnZX%(jeWB
|
|
zT!I2a{KtyXqb|^n-xNw;b@I%XCOWVXKib*}Xw@1i<?Q9ZJs(8I-JI9m*P9Rj+X}%<
|
|
zrsRB=sv`QrlO?pTKp-C-6@v`ZcTc0zs%^1(vY`~z8EL`7;rTgTT6tLTo_EFU*XZ+g
|
|
zP^QlGgm_Kh?-Ir|`R6|$yL)#NM9(~X3+{(SU&R!e#yX1ro6L!6Y5P}KEM8#nY0UG|
|
|
zI-7h0-bhJIII@Y9Ko|Wu7qP}fP)T<{28-T1_mbTBZ`>rI#~Ei*ii&6z(AVE?(}k_A
|
|
zE9Z@mj7HF-ch46I0ipe3gapRj{=zk_J1E^b_JwdrhKi4ytBuwP)m>e$@9v`A{1N{h
|
|
zwUN6H=_W+h(a?rGaQ%%LP5C4)XiZ*`1uUwgqWvk`LyDD!Ps#Q5oI($KDJ%8n5kBi-
|
|
zghsLx`~mf<>WT)6-cJBbp|htk1NfkZ@e#B4@l?UH7!MDMpO?1NETGk_Eg{z!N3!D<
|
|
zWg8gtgS%b(0Bg7dw9u35xq)1vNdnM8iu7Eje*u?#sZ~%^q*HDaZC?5z4ZzhSA%ndS
|
|
z4&$M&7(|(9nWY%<jgk8_GM^FTg|SlXZlmIsmU#4_Ro-#1zn`Qt)Hp3dI>QShCnuN0
|
|
z`n9&UeypypUgx;R+x;XM#8uDM{p`9~j<49)^dotHJVO*A@HL&g7F={FP#trj@{dzm
|
|
zeQUi<SFsuQ=RF$2&W>qRWJ&pkKkA1O-|vOf8O1UQ$$0lIExffio|}F@ROV#MXcPH$
|
|
z?$$kxAF@B#KT}u;R@SVyIO>1sw1!i?C(_013w9@?8$bKaLQi34zC$g*^}F&(%NEO6
|
|
zQzD-^6}HQMnGJ{h$J*)HjSxjblWegsW&rLC8Ov_r_20jLjUS$Ptnm|p9fK%r0j+4;
|
|
z57^mjL&lISh8>DC;eB$B69$h4XxE3qU4T&zUpDeV@4g>or%D-x@qhie>6<d}0Ra)Q
|
|
zbII8MVZZgP{TRj-9X#19@Pe?v_M%s+Uix_TU*lzE^yZF^ry*zf6QSSHe9^(ua)T)g
|
|
z3lz|%@80!4$B=VVO7;IWqPV%b%KkgW47l&_(1)K0+uk<a*;UoE7kYSjko19zhLmNZ
|
|
zkxYSpy&?T@SamHIo#rmyj=ecv7CpF?BC-~S=^yE3xPGs_UgdYt&qNX|VG){VgLNA0
|
|
z_=gE6YUFnmp^+Cj!|+SiGz0r2+*s=4q?3OLrpUdCc%@~9rhLw2YimzdYY<){TNOgQ
|
|
zP~gtaj^OiA%!F5m6X}g(2=Qgw{QI9E%0NU?F7BUHIB~N_=NJ@G5i|U{eyBC%P2H7+
|
|
z)2Z?C7+kSW|Lq^3ad(>mqD959ck74(h?S0BA0}YQ18d?hr6}%}y{%ZNJ^-(?=Op~;
|
|
z#2-UNh)jH9>RXmv<m;Fv4ERg;DT>PJ<VaWa@ea?1=ze9YeHT5jn2DkNKps7vAw^~-
|
|
zUZA1a-t5X_&N}l-vL7S#O}(Pw#U+mzRaQe|UKVh))g=u*qU;-|?t~;jAPF8bq$i5}
|
|
zO-(u5x*!M*g!@kNsJPN-jY-_Fczl!cxtz>(Y!8(uhyW|sFpyvv)AaNeljHj^Fx+RC
|
|
z!`@c->W1C^FUKHmG2w_atkdsMnzY+l!CV8havQ8-Gu)<8t{#V*2Pwp4h?ayXsi5Z>
|
|
zo!guta>TA~iv#iJpQkN>#)QF%As@2WgU&V_Y^qm#E*O}M_ijJfFWq<OZB)JOp0y&C
|
|
ziVdtrh6gE@CCeflMKdV!Q~5LzkT)py2<#o(V;}(=RHo6d?KeyMA%0ABLt+m?son?j
|
|
zd}Jy{Mikh2Cde*;KknNM`8?j|e_7Hu0<j1q1LUpB<FinspM;Xq<gta9JQg~hR<eh}
|
|
z1)Dd0n=bikPhI8&CN;lq{}*H9Mq^~F57(naq@=WsZ!3W5*hp}6&2(6{R~pzhVC<5W
|
|
zSx3d5qgk_+Q>}ts)-l4>D)kCqJJ@MG2$69ph0jzwI8ry1u8D@CyinC$oT?7S*Z}Eg
|
|
zYs}PWLqr4u@)w}#!{cMx;KxO6W2H6~3k$laJjAt+C{0mmCRnfs=OJYbh}HMh&e`#>
|
|
zj;jrpjqKCh41OK{FOS`@_sPP$iCm46G^EMNk8(l-1f>!gEV+4vMVRZ#8infUenP+k
|
|
zL^tBOH<Dy~_q00gFa0MCF2!V_H~B^qX7J|lG;N2kCTQLZ>F^=)k&U-Tw{gfijqQ&^
|
|
z-RHHII5yp}2|o8pTsf6x7$teW9Em!~iy2DN?D@|U)g%I6VG%JBO$|~;c~1Q^3|x`1
|
|
z6HRbq1#~Ke)wWpALcc&@P;m+*sGavR0{aOx3=IwUE3YPWAwV45pzD$~02inxi7(6X
|
|
z$zk683M=_r#M*+6fQ)&FK0y|lm7JLwS)K=t&ZJk!U_-y%_o@fhr{s37MUEQOF*M)3
|
|
zB$;4>Zx;Xk*(hwFjb>1iJ1f*D#nyWL{=>{2|9*^vCNN!%bF8Oe<`xz#s;jFz<K{4R
|
|
zUiG<loryQZd^?a`T<DWCEaU9ORMaI$N;;k@N!r=#Rvq@*TRyKtm;5TGUEW^q5ck@x
|
|
z#5u;EM<(ba5eQ&oREnC@fH)6<z(f@ICH?es$@7jwt}*U@^#kS8@M6loP;)th%#0`-
|
|
z8UzjlO`nmk72w=Mg-7mz#%l}UcH=&7{FDEbkCr4W*<{QZTi1pZ9!M7#FJ|!`l%5kP
|
|
zof2j0gVOFSQlJKFE<Hxbq~B;Y+0iI-AZ&9MAG7x?dMU|&97E6?yqt~dQ-aZMA!34R
|
|
zluH+&C2<Gu=jV67&mIt!Ao6G<{iG4^Qzuik0#}KVP8A%%GKu8Hug8}obm-2tQ`P^u
|
|
z>?;I}4M3lL;!fy_;J-E96O<!9q%smKF{YakPa);H$LQ>f+;sG%K=fZdR)99pJ}fM(
|
|
zq%(s8UrsEL{NrdF`!#RY+VjFyPpE_vtqPMM!MQ+QnE)+_g9Z^{4^;k&Sa<mC?dik&
|
|
zG&>^=w*yuxB_*Z!U%!3{_9Qr)Jfz4<bDOz@=g~Ht`yS3s<dx-tdo~wm{04hN5Tkex
|
|
zPfl`XUl*)bJ66jjo<*o_U~tI6QYwUSe|WZnI}eWv50pH%g?emZ1rEz5uO??N<&63s
|
|
zZ;nOjyGDxQwqo!Zd!7>IeS#io4oj_Kqhq`HCUub|Ke!v$1-$v=kc+O#rlCej?%dhY
|
|
zxxKUTsFPG1nfoFp3%7@gh9S?vM<nq?jd$w4RoB{jAO3JpBl0vfK0bc5opGX{7^jky
|
|
z_d8xz0q+C~RxW??%>0N27#*fpJyaX;Vy{!pt*}!9_mX9uC#J5RyjknW2Dm3dCvZYU
|
|
zSW?0kvI9!o2un}*%`AYhr^CQT1aZF=-Nt^atn@Kt%b2!hT(pK!|MclbBv3-<+6{>_
|
|
z8toMfWc9rpOk(8|KW>Z-k>Fr(xc_+q9ocf`8!_n}XYUrW?Ax|*_|=5m*4F0V+46wJ
|
|
z1IGS^Z5t=0Zj86J2Mf<IyOfR^5fZU$qK8D`Linev1K{10+j54=1@ueR*W)wENE<#=
|
|
z+5Rh068E7G$0<udnuh-mn$jG9L?+S;3#p%Pe{{doFt_fX{J0tW-&%ay?khH<Sd~ew
|
|
zPAq0e6zI$tgLVhxa@RMdkQjU-@%JWnbVm$$0GsW0Ddqc~O7P3c%I3<-y;IfiXm>Jc
|
|
zUq#WKCfhoB<;P2&&`*_G4^_0uqDR20m!>T8ay_rxSzA&9_v5##g6tzXTkx+KRfz32
|
|
z9vvpp?+YxHTxDthCBu7)&Q052y4s9*$M4_2w-OdPyK?F-EBoUuSsIk@@(!gA*A_!0
|
|
z2eu1y;-Q$Ut(M>8FCOtw?vZR-%*ly^x)<95vK@P0tJoZws@+M*NGhg<JM4ut*Kbs=
|
|
z>_NU`!}DZnWBHQz%*@6))$BWN;EM0xAF+B4Mph#S??J?K+&viwPmes*n^HGDL9iBf
|
|
zCk|mDu46wwughN!isu&G((DO>Ws`(VLY?^#w=RONx<Y#sLz9wh4(stkQnM_%!NUOu
|
|
z&}G0mmW>UgFGby--Y=5NJ|(>qXOS`;lZhmXyMEyBdVM@jJh71E-})~`?t4w8^Kwy)
|
|
z<+KACjs!F^TS-;FT24_iWF+=l(<z7_pRw$iwy9+<gk-ore&fdtevcw1eQH|T<onD$
|
|
zLhx$6xs1l{MS6hA1MUdULP`UqE4(3q5_(9@wab?3b=tf<var%-(>nR}<L>j7U#;Vd
|
|
z)IT3=b&}A}1PU<W2V}5C6E;reR}0F!X0bE`bqOGHr(_S5Ff&I$28hko?)DBGARKL{
|
|
zAm)UP#K*kfCmW6@r<FnhI5QD@jiF^U42)#8<{z8>KFa6DKfgHkJci!~7u?a%k<bAO
|
|
z39qF71Xeu9;#EdY;3|uBKmbh+R>9h7Rri^{y`|;;xNDoQbV}+oJ=LdApL}|77o@C=
|
|
z;~aed)XpbrMtt1x3gHPW<dNqflNn2eUeC(N^=;pyL~v6xFfg#>xbliQH4nKBCew{9
|
|
z*-_PTyn~`1VrwKcc4ZrhI^!MsZ{D0O0%O2!SHHi^Dfyr9*x*DGFKwc()b;q6nM*M7
|
|
zvA$x_?$BMJJHN5HIn9Ps{_7-sn79~BZegaa5V;s(BA<5BnU?^AeJHXtd)cIj_UCjA
|
|
zW|N@MjV~vrJz{sE0Dzv}tXxUDQAXm)1(kX7C_ZVFX%!TlZ850i(P1A0BxaJu)#LcH
|
|
zoxMFRzxoxw$bM=B6gpuMD#<QBON5;Wh=~6jUAFX-N8#S1bc$rbVVp+xFmaSImrA+2
|
|
z3)_Z?yLbabpj%w$pCG=tu%JoH>vcsa^00?%=D+T9-dQqV*=zD|)W!3BLun2&^n)~$
|
|
z2_^{i9~sGXOAsF_S=k&4mWJ@`mD+G%MiPTl<D3N^Y#a?Gmws%y>huomboeFNwHb(<
|
|
zVpVR!mwf;JmpO3JL|B%L-!;@7TG}+`HZA;-{VIlQGY|T=f|!9!S=!c?sq5|KeEQ*~
|
|
zm!1xeZcJPbSsfjU<fs*ikm;&K=qr{7NcyzX=8+*7<42C!-ATj|Xkow*h~}Q*fk(}~
|
|
zPU?p-;CF<$gC5no0ic(7fcF>9e>K|=Ni<+YgrIG!|5@|Z>4bjx+`1j^O-{QK8XARf
|
|
zUG$nLRiTEtt;)9F30rvw>nj)@vCF{$d7>o2n>}~Y2^^C79l@s`uXRZOcuy>^%2@t-
|
|
zRGv={pKlDXFUgvG_^DWGR==il1rIzn{$p4r(FVOQxZi!_*Ksfl2hR{Aj>01RbFAM=
|
|
zpr0wzMwlOwlkt4|JLK)$>VL+{4nv>^`yMa)T;(9f*B(9;{T+)_=M4dN>M&&hS-#(G
|
|
z)-sW(WxVkHR)`x#g)25Lu7qnN;~Q-bvK<Bi>DZ=;^fyLy@okDpvt&ZU{!U)WVtmnp
|
|
zAN-CzM{jPFWep9NAKDDq@=kynkGi_GQ@Z2y_Wn)xc_q3-&+9`qdGy_{PF-2c^$)%x
|
|
zd0sonEJhtG*2|<U!Py~$;b=E=Fv&a+%q}FBi9InZo|rkRFM==Jq8M7{pVAwZnQj{z
|
|
zxE3wSx8N*L5D*YlH8eslFJ1E`W0|P+yL{VJYFJm`L<d8I_>P*Q-f_3`Akk96HzBz2
|
|
z!5tnJaCcA2hGQrSw*{F)epvfYX?7toP=O0dN<w4xSn<TAAv<v(v(f35+?0KJ{v=P>
|
|
zizY2w`>O@4Vqff!dBhQ^><#TjMP}loM9ProiD-Og@$V=*zQ|Avg0D!+96lr^u(1fl
|
|
z3J52PHoJYDdvdiIW?q?JIC*r?88VruLx#bp0ly<EtEzmzbg=g!M^Z*bN7G1c_p!!V
|
|
z2n6Su_0f-h!k3Pgt;AQCp!8A(ONO`yVo9N&85&Nt6RWGh&>s39v$(c6uC*j}2IFFh
|
|
zViOX|K+DH18cd9%Rgjs$*sXuoW<>p^Fv-7CV|zpgTUnj812pyyX-nhA4TZ^UyYY9;
|
|
z?}BOarTT1q;0xSTjV_DPWE11?Y2+wSA*ybzebDoy8JwhznKa6SvYxE$WswX7Z6pG$
|
|
zsA2GgHFFL3^zA@XTYK{a+6$Q8di%@1-|q9U15y+~R-L7Kwx8*xr(<KeA$al9V~r0;
|
|
zR4vK6dswz^{@t(o(S;W4g`=z>FP{g*JDPa`e((jSl#~?Rx=3ne(nLfeP9k0grubJK
|
|
zU4euzZqt~$Cl%k^{-!e6YQZi|D3#+MUS}VsYZ)0S>y@)kyqRI?A_esvAu-{`1Uq@!
|
|
zC+b`wnMK&<_mitl+k@e*$*{&S>vayX*>D>Q5sw2FZ?l(8ff%(8lo<^mBMrwQXOXe+
|
|
z*7sZdWzBTIwZO$y^F)qZL1XbOMY<@M_a56y{({Vg@YN<_y}toq41V%~w=+4ZQvg)X
|
|
zVw~l$z-sId^nKU%dlk7W(mG}eS&KV2BdYqNJnX-p=YrG&&`_m0fzA_|iKD${5?oL*
|
|
zdS$heR@%Q+(3!!T&k;tIN|v2j=UI))rgkvyC7MTTrKP3g>Fma@_R0`GE5(tL%sS$7
|
|
zG4<G{z<=awc^y@m*i@AvEb;NuK3Td(#kwE?Pp4PGgyEk?)mkZA0CG)1H~nam;OHy^
|
|
znGx*W%cw)|7dCVl91aVm8>1ag%(Y(xZ5cjlk=R~(3XC+$25r*Fo=G5Oh<FY_42R=|
|
|
zue7?*+O~6lB~I+3D{-w`K{9;M*&qpZATfcr)9vphi6b*Nr@1?JGQcOYrTIR-6;I|0
|
|
zgVVQi`b9l<%7HgU&JdtNN_`Oim&~)ZhCF5`%5$31@^YibB5)G-c+M~}7KvG*ux-VE
|
|
z3y}-5F3)S)R*&sXDc1ScBk&1363zt%r$|+ACkT-uljjVAJZ}8<s7=F|Abd-7d$PLg
|
|
zS&h>GgR}i!nDoG?^sult?Eo*x$x6CH-3L@LtZ0dfq!Bbbw-S}RwlN%lpH8c=4l2qH
|
|
z1wRszHSPh~=esnWvXD8B{D4<}?}6cA+@Ob1760Is6`g!zl@WL(L&={LA}SxAt0>Tw
|
|
z%b7<SOz2?a4~+!akApjVHjh>i^&yNKM;(vGcN<Sf&AXV>wuxAK{g|S3Y1&pH_6U1G
|
|
z3M4zx5FU=O;=l_?VzQ-~bx~xN1axPgYI0am3d25BjYmfSTX7Q}==Vcryl6@Se0(Jv
|
|
zxKW_o%H`jdnC7QXlkFbCsACHN1Dx=0gf<~@PW-&<=`1H<kp3Ee;L6<7@+MfgKar*z
|
|
zKG6%MqS37pG+^K|h<_I=D#SoV9jaVTJL%>d)@#ypH7%OpalDj-P=ts<mf5I<tc%M$
|
|
zwqK$_5?Vu$GP?{5cGIBplUQN7<vY&JMOisLL*b6^>+3^~yWs~TV}BD20HjkW6zc1L
|
|
z0#HzMkn3JV%7N-18_@tgE82*YnmEzxirriDSx#_|<|q1vL{k}7>^mRzO(ueTSN2~H
|
|
zG}kxp)Qn!&)><3|e>62+GXSpQKcemfqU!<SHW6kia-R1eVlE`-(RUe%Z0%uTVe?%P
|
|
zmr>&BHZ5Ca;DT<63bBM&uV1BDS?MM$M;x8w>gShAPMxJM^BbMZn}Unm{OC9^4x3%%
|
|
zlmX8!km-u$<EVfJKu(+M+HRbtKi|Ftw)BZbQ0kb-YB3>N4fQXQ>jRe`7)3+RFGjhz
|
|
z18zf(Fo2<>YV^7LJO^UTZ2Ivd#mpN}o?7pBV&q=f%ID>haV7M8R3jsF*@a%iwIy>|
|
|
zsZ!-y{!%&j7`B?W8TcF4NH-RHH1xZ{;7BsA<#APu!;cND)te)FhoXz$BIU}2&^7WP
|
|
zT}TX>ZO58$VNPuh6JV7~s(W$vAj`^%AtUamex3YdVl3~4+pqk?G)qUibNMrj0*M25
|
|
zY>5Ac|Dnv6xBQmV#$3JA?&HTN(lYl~J}@$l{*TY^kORrCB)3dDO}^^v!dcLf^CHty
|
|
zanjllIQeSLmpuG+h&ae`r*v!C*0A&W^a&q>93?BAX<LcxXTLTY2s-6mH5j{so$!U)
|
|
zu}GH={~iAH-oKo{`^-k$uv|gU@UC4_<$uGT_*PO2t4s{LaCE29O~fBc4&VlcPd2*)
|
|
z#zvJQFe!(OUoSHPjpu{IuNCg}wvAkG*g_RT_(rGw(0Zu9j`9{G-~QKRP!RaH-`)BE
|
|
zvb7r!*44{1+{Ru&`NGNjM?^V`yK=J!{8AiUDYu$_ww(r(8nuu2!3mW4qlNqo>zG7n
|
|
z2*3TGPIcN`-_hY9&oaiv#fiv~>}7`T`4=pInEqWX*3e8+yPm^9h-tr&ts55$l+388
|
|
zW)~F}2JH!}VLbQ>?6~H@&k`MnSsTeVj0TRVP4jGbP*!!CwM6`Z11c)yI2w$+R0zxo
|
|
zT|obYS1&&`{>>Z9(jnVU&=yI*%PGe*f78ie*_9oap?sd7fx7<i@Un5>{r^WT>=XHF
|
|
zl`f{=UJEn2?tRw`Fem?eRE6#*nOes(ebRcmaK3~a3{a3EyE1zXSF0p7I_iDJ&%;3V
|
|
zU;AS}e?*mH#Yh2P9E3QBigIqu2iXf=@t)2+I~f*_E^JtEP1@IR{CBfTj%T}E3e#n%
|
|
zUa{@vU?D$l4DEANwkkK@ruP4ta)E*e^KLGg%$PizyPmHvKNMWtuJQ6sPXY=(1m#>W
|
|
z7V?9E!Vj}>a|KfQx5ESpH+q6$@gAp-P#~lbz`aj1_?xinN>3o8b2-Z3w>UZ3QZ}W0
|
|
zWg-!>p>AADDcU^4;0*L4UFgB0QLlXd^y1E&4>txV!T|!`RwjZGl`;-4ZgFf>luHIy
|
|
zZ8d8Rh{I3r!g-ht6mAZxMB<QvHOCHoM?w@=LivZWhXfo8s>6VxRqnA0UY`h|mJZy2
|
|
z17BazT$jMKFL3J6Ue_HL1^)4s%$Jj~Qx~1HG#tS@kwL(KP_ZI3d<ID(%K-Jz%rzpL
|
|
zsA)k#LG81%YTeo!sF8uO!$+DGU<1Nfx9Mn8P7WN{%pH&do{3^Xz``S44|M@5Jl{RU
|
|
znCqoV1?&LR)04NzJ2p@Q%|yHrE%pEDSBC<fWlAZcHH^p5r5BjvDjdb?OI|_IH$bi8
|
|
zEZ-8Ug1a>Wz0SH(sqj#-*TNGsIWqPj>cj?!GyWvfdEiNOu4$>MIqL=F&Cc0{g*~L5
|
|
zA1wt)=_zMFUkCT5$l!G{1-Y9QtGQ#qm5E(3fYPms_EP*sSVI)bfXN|uNO`BqVuCvd
|
|
zv)z8IGRgtM1<_trndVhQ^xA)wn~*W~#d*X@E=W)jcQWI8+?kdzHe;DZ`%+JE%gE}m
|
|
z6H=FO8rJxM{N90S=Gi!Mel)TyanxPa;E}C?hJ<QZq(s_1DBn*w@r6I}eqF<^`B7!9
|
|
z<>l@e9UWad->;S|v;axgFjrY$z3(rV{MiJ}<CJ0+{mbYzcbmjjreGu1p-RaeH~n0n
|
|
zN%H*>3M)t;Q?P5wZy0e3G{dcDO7n}3slDXLMrB$;#*W@Qv)D$=?Xs$F(8eT<r=NZm
|
|
zQ(qCW$1QM0^+pQvqF2C5h>cyGIQ~IWgD%Gn&E>F9y#o>cR-7spE;Rur<_E~Pu)e0I
|
|
z#&y1|@8D~8c55<|KMf;&x;hg!A%VOZ38_+uk`jH4#=b9M&xcpxV-7cMN{jXVRnKSe
|
|
zlKJJ%=VBV{$DNeI1QkiA;DfdVT?$;O#22z6v6bTK9)fjrfIh!Hq__l~KzuNqT{&kA
|
|
zKs@YV6^1ZLGjTgR%(=NHS-DvWnnP)NM#qbHINqmQ<pv;&O&G={*ghh8^NuD!$&xpB
|
|
zUaWmlRE4t;%CCAT`7Wu|;O#HN$?fUQI{s(5KHb_gg*+-&Twj`?7#mNLR5h4`7-O5G
|
|
znwYVh`W220J5TvL5iVFsek%qw$WN*X8HwusSg=%#UcHSPsaYnns5*}s(}omD=Idd@
|
|
zcp!dv`2^$NMQ209b#6d1hn7`TFiDakunCFNsOl{1FRRlqXIYGI(RupP?)F_bwx~@v
|
|
zK25H83lZ(&L^?qpkUH5YgKR?S(4rW4cRl;SK27oWXak-FJfS+MGH~P9l!+jjE(QB2
|
|
zT!p|EsR7EJ3o=>dCE5??co$3nuikqgm=s7*#Kd*+j_weKrZjMeLeHEoiJm>zuDRU`
|
|
zh~ggr^knn<c9LCD(ZRt%{B|L`TFuhy2nE%WcC9UvOP<FLK>eWU!Nn}AQt=0Id6Hk;
|
|
z4bJqse|V$H`stT?NS0yreYvaZ9YF!fw+N}{3#yXRU!C7?exl35BDC%+!jDMGT^DN#
|
|
zN9FGd#5t#;$h}5UgQ?q-Gr15>C6=nLUszle9<+_!!oi_m@_L^-R>_Qty7_g|C%m|5
|
|
z-7^5X5V_ARi?h9_LW%2vByD3X_IvUktqBv{%SYXO1&;e&O#Ll_cfC`Wv1u+l_#RI<
|
|
zQ5K<R7woH(6ii>ly0;P`%TXaQN(heOg~>V&L{d+ZDA%eq-UKo#1)$rkjSm=nzAE2r
|
|
z5--RyKhxfXoGVU3^ab{5XGlyL1+26foG)4H<n?S$srX0vX6KzP;OowPO*ZX%@I+1B
|
|
zd^@lo9?A;<O@!{!hM0O{WRMM~5i4ZzMz$S+?@pI$+h94nzP-Ku;G^TOYaI;@+>ZvN
|
|
zG@&I3h0fnK5lIjcrg*XxPy1(gK3_TN`&VYnxP;C|j$~0rT$0f|*#=OzM^NbE-1T5D
|
|
z%Csnt)n!sx3N#b(8G&+G3W~Q_B#StA6jZZ=p#wuu`DrAMXm{T@#S;ku4Dme@{Njmk
|
|
zCtrh3z6O>o)~o{&Htx+6kn*)$NNBH-biu^a<WFnLup`-{UAH45I`7I&(sBY>YtWUq
|
|
z(G>4rCEKr#tO>!x8A@%W@6g)Xs%2Hq!y#Mbb@9R2@GDWi&!{jhZvzQ1D9nMuPoOS+
|
|
z+cj{9nx5X{jJOIavbFf)Kz5Jnbe5Bu#(XE-z$j&iaP%c9W59OoT0~|N#D*(N2kz={
|
|
zs(|)nH!_+_g1)#ZH2xk>ZTG#6WN#qa3BxZM{NWxq`*#$H255k6Ky?hw*hSA6`c_fl
|
|
zT@Ua%E5Ez3;~`kQFmrC#$Nlvc_Uy3#yzhd-6UYuuIwgIBZZC-`dwOBJbfurL(FfhH
|
|
z{YkjE+9OrOveY`{t{sGw&51YO1@{iO4)Ki=!Z5#q=m_Hi)_j0`>?;t2j);vv%BUif
|
|
z;wpTZdLQLsGvZ()DCdxYudn^Pt;BZ}Rin$4F8h{R`HxT2z`uc&aMXIQOvwgA5%{&)
|
|
zFW52MiN!$!EXgx}Px~e1!EMp;#&kY65oDho95j~!qD%YJr`+aK4jCJ4UJ^;q>w@Lf
|
|
zvDfg|M`S^@DGxu+7aR3Cx#;<xgSDhwzwCQFIk|AAJB5B~mR_Gk(_}Nh)Llbo_PTq*
|
|
zKpXMTD^GyEo^B+xzR09t;)E_El^4Cc<Kvq++Uz8RmrWYXyyI_c`->%?advj&1~L-m
|
|
zJqCP9&TW3migV*`Z$#)Qa>3>Jf)g9D6Ki2<I<i}IfTAEzE|UIp4RQWwg_TSlZn09=
|
|
zE|{&Qi(^_E>8P@iX(us<lk2S8)o-+`jX3TqT@qu1J!6hFJc$<zY3b>o)hic8Dp1F<
|
|
zeF;(n8Po8A*~^T{De(<avPjs6y<_Gz2B@0~;F2Mwv*H|*Y`w#F#O7bs#2<?tYX^_4
|
|
z_8^68Yi=w7O#3;Y=2-K^)&J8`g%MZN)bz1eP`L5w?DTnrl-(^+z&W4YztC_*O06i-
|
|
z{GQG1d)tx$D+D03_+eow{(8DlwY5Du1x{6UPm3bS$kqWgkq~g0tAde@t;WJAyXsM5
|
|
zGJ`JQx>J)Z2nqLl@Vv3yoSlGwq0aeOg4ymI(KIkTeur-=J-yp9z?qe)it6gq-wl@I
|
|
z0D-_I{|T<5kwD9uH3yf1GWXp5*8eOgJf*q0IRoK|+r{}Fug&0WpNDKMTC@(Xc)9K8
|
|
zy`lByMn!1fnY)1KYP(0Je1)c~WilUuh<&Q8^OE?L9Q^xK*Y@M$`6D6TDCZ^@l8{|}
|
|
zxmmNw)mng$hYBii+&ZqedxWT0<Y>dnV#LG4zC%+kzcK+-??vEHT>Q-T8zu<!_QuSc
|
|
WX&3$!%>|s_1IbA#OV)^+1pg1OmmZn`
|
|
|
|
diff --git a/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
|
|
index b2d510459bcf90a3611f3d91dae4ccc3d29b4079..7a052f6deaa30f8a177a2aaf172f9da6c308a22b 100644
|
|
--- a/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
|
|
+++ b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
|
|
@@ -37,7 +37,7 @@ public class VanillaMobGoalTest {
|
|
}
|
|
|
|
List<Class<?>> classes;
|
|
- try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft").scan()) {
|
|
+ try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft", "org.purpurmc.purpur.entity.ai").scan()) { // Purpur
|
|
classes = scanResult.getSubclasses(net.minecraft.world.entity.ai.goal.Goal.class.getName()).loadClasses();
|
|
}
|
|
|
|
diff --git a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
|
|
index 8665e2740aedcc2895b0e2c44ebaba53d2a40568..b7e2227116ee0a08826674d8681fdaac97efb0ea 100644
|
|
--- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
|
|
+++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
|
|
@@ -45,6 +45,7 @@ public class MinecraftCommandPermissionsTest extends AbstractTestingBase {
|
|
Set<String> foundPerms = new HashSet<>();
|
|
for (CommandNode<CommandSourceStack> child : root.getChildren()) {
|
|
final String vanillaPerm = VanillaCommandWrapper.getPermission(child);
|
|
+ if (TO_SKIP.contains(vanillaPerm)) continue; // Purpur
|
|
if (!perms.contains(vanillaPerm)) {
|
|
missing.add("Missing permission for " + child.getName() + " (" + vanillaPerm + ") command");
|
|
} else {
|
|
@@ -57,6 +58,25 @@ public class MinecraftCommandPermissionsTest extends AbstractTestingBase {
|
|
}
|
|
|
|
private static final List<String> TO_SKIP = List.of(
|
|
+ // Purpur start
|
|
+ "minecraft.command.compass",
|
|
+ "minecraft.command.credits",
|
|
+ "minecraft.command.demo",
|
|
+ "minecraft.command.ping",
|
|
+ "minecraft.command.ram",
|
|
+ "minecraft.command.rambar",
|
|
+ "minecraft.command.tpsbar",
|
|
+ "minecraft.command.uptime",
|
|
+ "minecraft.command.debug",
|
|
+ "minecraft.command.gamemode.adventure",
|
|
+ "minecraft.command.gamemode.adventure.other",
|
|
+ "minecraft.command.gamemode.creative",
|
|
+ "minecraft.command.gamemode.creative.other",
|
|
+ "minecraft.command.gamemode.spectator",
|
|
+ "minecraft.command.gamemode.spectator.other",
|
|
+ "minecraft.command.gamemode.survival",
|
|
+ "minecraft.command.gamemode.survival.other",
|
|
+ // Purpur end
|
|
"minecraft.command.selector"
|
|
);
|
|
|
|
diff --git a/src/test/java/org/bukkit/potion/PotionTest.java b/src/test/java/org/bukkit/potion/PotionTest.java
|
|
index 83226ec2fa977819e12a499eb3765232543c17b3..a742774dabaee0629f4e6adabee5f3ec4b3be41c 100644
|
|
--- a/src/test/java/org/bukkit/potion/PotionTest.java
|
|
+++ b/src/test/java/org/bukkit/potion/PotionTest.java
|
|
@@ -9,6 +9,7 @@ import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.world.effect.MobEffect;
|
|
import net.minecraft.world.effect.MobEffectInstance;
|
|
import net.minecraft.world.item.alchemy.Potion;
|
|
+import org.bukkit.NamespacedKey;
|
|
import org.bukkit.support.AbstractTestingBase;
|
|
import org.junit.Test;
|
|
|
|
@@ -47,4 +48,27 @@ public class PotionTest extends AbstractTestingBase {
|
|
assertEquals("Same type not returned by name " + key, bukkit, byName);
|
|
}
|
|
}
|
|
+
|
|
+ // Purpur start
|
|
+ @Test
|
|
+ public void testNamespacedKey() {
|
|
+ NamespacedKey key = new NamespacedKey("testnamespace", "testkey");
|
|
+ PotionEffect namedSpacedEffect = new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 20, 0, true, true, true, key);
|
|
+ assertNotNull(namedSpacedEffect.getKey());
|
|
+ assertTrue(namedSpacedEffect.hasKey());
|
|
+ assertFalse(namedSpacedEffect.withKey(null).hasKey());
|
|
+
|
|
+ PotionEffect effect = new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 20, 0, true, true, true);
|
|
+ assertNull(effect.getKey());
|
|
+ assertFalse(effect.hasKey());
|
|
+ assertTrue(namedSpacedEffect.withKey(key).hasKey());
|
|
+
|
|
+ Map<String, Object> s1 = namedSpacedEffect.serialize();
|
|
+ Map<String, Object> s2 = effect.serialize();
|
|
+ assertTrue(s1.containsKey("namespacedKey"));
|
|
+ assertFalse(s2.containsKey("namespacedKey"));
|
|
+ assertNotNull(new PotionEffect(s1).getKey());
|
|
+ assertNull(new PotionEffect(s2).getKey());
|
|
+ }
|
|
+ // Purpur end
|
|
}
|