From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sun, 6 Apr 2025 06:33:24 +0000 Subject: [PATCH] Purpur Server Paper Changes Original license: MIT Original project: https://github.com/PurpurMC/Purpur Commit: 61d7f5590e79909caba55e9f00cefdd51006d015 Patches listed below are removed in this patch, They exists in Gale or Leaf: * "Rebrand.patch" * "com/destroystokyo/paper/Metrics.java.patch" - Purpur config files * "com/destroystokyo/paper/PaperVersionFetcher.java.patch" - Rebrand * "com/destroystokyo/paper/console/PaperConsole.java.patch" - Rebrand * "com/destroystokyo/paper/gui/RAMDetails.java.patch" - Add 5 second tps average in /tps * "io/papermc/paper/ServerBuildInfoImpl.java.patch" - Rebrand * "org/bukkit/craftbukkit/CraftServer.java.patch" - Add 5 second tps average in /tps * "org/bukkit/craftbukkit/legacy/CraftLegacy.java.patch" - Logger settings (suppressing pointless logs) * "org/bukkit/craftbukkit/scheduler/CraftScheduler.java.patch" - Rebrand * "org/bukkit/craftbukkit/util/CraftMagicNumbers.java.patch" - Rebrand * "org/bukkit/craftbukkit/util/Versioning.java.patch" - Rebrand * "org/spigotmc/TicksPerSecondCommand.java.patch" - Add 5 second tps average in /tps * "org/spigotmc/WatchdogThread.java.patch" - Rebrand diff --git a/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java b/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..15a226e3854d731f7724025ea3459c8ace07630c --- /dev/null +++ b/src/log4jPlugins/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 formatters; + + private HighlightErrorConverter(List 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 formatters = parser.parse(options[0]); + return new HighlightErrorConverter(formatters); + } +} 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 a786d7579455c017c8ad79c97b1507b66f8c0800..c26cf541de853d380048119057752e1ccebc89aa 100644 --- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java +++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java @@ -27,6 +27,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 - Add option to disable zombie aggressiveness towards villagers + deobfuscationMap.put("zombie_1", "zombie_attack_villager"); + deobfuscationMap.put("drowned_1", "drowned_attack_villager"); + // Purpur end - Add option to disable zombie aggressiveness towards villagers ignored.add("goal_selector_1"); ignored.add("goal_selector_2"); diff --git a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java index 41c95f00b4b2bea6d31f85e268c33d7f6184823e..2eaf27ddf0bddb23b8850310c332b202b1d7e69c 100644 --- a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java +++ b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java @@ -70,10 +70,10 @@ public class PaperPluginsCommand extends BukkitCommand { this.setAliases(List.of("pl")); } - private static List formatProviders(final TreeMap> plugins) { + private static List formatProviders(final TreeMap> plugins, CommandSender sender) { // Purpur - Improve output of plugins command final List components = new ArrayList<>(plugins.size()); for (final PluginProvider entry : plugins.values()) { - components.add(formatProvider(entry)); + components.add(formatProvider(entry, sender)); // Purpur - Improve output of plugins command } boolean isFirst = true; @@ -100,15 +100,49 @@ public class PaperPluginsCommand extends BukkitCommand { return formattedSubLists; } - private static Component formatProvider(final PluginProvider provider) { + private static Component formatProvider(final PluginProvider provider, CommandSender sender) { // Purpur - Improve output of plugins command final TextComponent.Builder builder = Component.text(); if (provider instanceof final SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) { builder.append(LEGACY_PLUGIN_STAR); } final String name = provider.getMeta().getName(); - final Component pluginName = Component.text(name, fromStatus(provider)) - .clickEvent(ClickEvent.runCommand("/version " + name)); + // Purpur start - Improve output of plugins command + Component pluginName = Component.text(name, fromStatus(provider)) + .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 = pluginName.hoverEvent(hover.build()); + } + // Purpur end - Improve output of plugins command builder.append(pluginName); @@ -126,6 +160,23 @@ public class PaperPluginsCommand extends BukkitCommand { return componentHeader.append(Component.text(":")).build(); } + // Purpur start - Improve output of plugins command + private static TextComponent getAuthors(final PluginMeta pluginMeta) { + TextComponent.Builder builder = Component.text(); + List 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 - Improve output of plugins command + private static Component asPlainComponents(final String strings) { final net.kyori.adventure.text.TextComponent.Builder builder = Component.text(); for (final String string : strings.split("\n")) { @@ -186,25 +237,25 @@ public class PaperPluginsCommand extends BukkitCommand { final int sizePaperPlugins = paperPlugins.size(); final int sizeSpigotPlugins = spigotPlugins.size(); final int sizePlugins = sizePaperPlugins + sizeSpigotPlugins; - final boolean hasAllPluginTypes = (sizePaperPlugins > 0 && sizeSpigotPlugins > 0); + final boolean hasAllPluginTypes = true; // Purpur - Improve output of plugins command final Component infoMessage = Component.text().append(INFO_ICON_SERVER_PLUGIN).append(Component.text("Server Plugins (%s):".formatted(sizePlugins), NamedTextColor.WHITE)).build(); sender.sendMessage(infoMessage); - if (!paperPlugins.isEmpty()) { + //if (!paperPlugins.isEmpty()) { // Purpur - Improve output of plugins command sender.sendMessage(header("Paper Plugins", 0x0288D1, sizePaperPlugins, hasAllPluginTypes)); - } + //} // Purpur - Improve output of plugins command - for (final Component component : formatProviders(paperPlugins)) { + for (final Component component : formatProviders(paperPlugins, sender)) { // Purpur - Improve output of plugins command sender.sendMessage(component); } - if (!spigotPlugins.isEmpty()) { + //if (!spigotPlugins.isEmpty()) { // Purpur - Improve output of plugins command sender.sendMessage(header("Bukkit Plugins", 0xED8106, sizeSpigotPlugins, hasAllPluginTypes)); - } + //} // Purpur - Improve output of plugins command - for (final Component component : formatProviders(spigotPlugins)) { + for (final Component component : formatProviders(spigotPlugins, sender)) { // Purpur - Improve output of plugins command sender.sendMessage(component); } diff --git a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java index a8e813ca89b033f061e695288b3383bdcf128531..96a377a07d8396f893954a3ba97fbf88d73faf6d 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 - Enhance SysoutCatcher - prefix not needed - if (SysoutCatcher.SUPPRESS_NAGS) { + if (true || SysoutCatcher.SUPPRESS_NAGS) { // Purpur - Enhance SysoutCatcher - nagging is annoying return; } if (SysoutCatcher.NAG_INTERVAL > 0 || SysoutCatcher.NAG_TIMEOUT > 0) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java index 43ae147ae15668d3357ff1f7ae66bcad241d3ff3..ecd9e392e9e026cbeac4c65bfdb7467f971ca92d 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -336,6 +336,12 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa @Override public Location getLocation() { + // Purpur start - OfflinePlayer API + if (this.isOnline()) { + return this.getPlayer().getLocation(); + } + // Purpur end - OfflinePlayer API + CompoundTag data = this.getData(); if (data == null) { return null; @@ -573,4 +579,183 @@ 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.get("abilities") instanceof CompoundTag abilities)) return false; + return abilities.getByteOr("mayfly", (byte) 0) == (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.get("abilities") instanceof CompoundTag abilities)) return; + 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.get("abilities") instanceof CompoundTag abilities)) return false; + return abilities.getByteOr("flying", (byte) 0) == (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.get("abilities") instanceof CompoundTag abilities)) return; + 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.get("abilities") instanceof CompoundTag abilities)) return; + 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.get("abilities") instanceof CompoundTag abilities)) return 0; + return abilities.getFloatOr("flySpeed", 0); + } + } + + @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.get("abilities") instanceof CompoundTag abilities)) return; + 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.get("abilities") instanceof CompoundTag abilities)) return 0; + return abilities.getFloatOr("walkSpeed", 0); + } + } + + @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 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 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.toPath()); + File playerDataFile = new File(playerDir, this.getUniqueId()+".dat"); + File playerDataFileOld = new File(playerDir, this.getUniqueId()+".dat_old"); + net.minecraft.Util.safeReplaceFile(playerDataFile.toPath(), tempFile.toPath(), playerDataFileOld.toPath()); + } 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 c92adb939892e1054f7b2d24070f43e6104dda2b..23bd30ad769507e3eeb4e397e57a2c0ac01f8b7a 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -429,6 +429,20 @@ public final class CraftServer implements Server { this.paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); this.pluginManager.paperPluginManager = this.paperPluginManager; // Paper end + // Purpur start - Language API + 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 - Language API CraftRegistry.setMinecraftRegistry(console.registryAccess()); @@ -1074,6 +1088,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); this.console.galeConfigurations.reloadConfigs(this.console); // Gale - Gale configuration + org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur - Purpur config files for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) @@ -1089,6 +1104,7 @@ public final class CraftServer implements Server { } } world.spigotConfig.init(); // Spigot + world.purpurConfig.init(); // Purpur - Purpur config files } Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper @@ -1106,6 +1122,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.registerCommands(); // Spigot io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper this.spark.registerCommandBeforePlugins(this); // Paper - spark + org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur - Purpur config files this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); @@ -1605,6 +1622,60 @@ public final class CraftServer implements Server { return true; } + // Purpur start - Added the ability to add combustible items + @Override + public void addFuel(org.bukkit.Material material, int burnTime) { + Preconditions.checkArgument(burnTime > 0, "BurnTime must be greater than 0"); + + net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)); + MinecraftServer.getServer().fuelValues().values.put(itemStack.getItem(), burnTime); + } + + @Override + public void removeFuel(org.bukkit.Material material) { + net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)); + MinecraftServer.getServer().fuelValues().values.keySet().removeIf(itemStack::is); + } + // Purpur end - Added the ability to add combustible items + + // Purpur start - Debug Marker API + @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 - Debug Marker API + @Override public List getRecipesFor(ItemStack result) { Preconditions.checkArgument(result != null, "ItemStack cannot be null"); @@ -2976,6 +3047,18 @@ public final class CraftServer implements Server { } // Gale end - Gale configuration - API + // Purpur start - Purpur config files + @Override + public YamlConfiguration getPurpurConfig() { + return org.purpurmc.purpur.PurpurConfig.config; + } + + @Override + public java.util.Properties getServerProperties() { + return getProperties().properties; + } + // Purpur end - Purpur config files + @Override public void restart() { CraftServer.this.restart(); @@ -3215,4 +3298,18 @@ public final class CraftServer implements Server { public void allowPausing(final Plugin plugin, final boolean value) { this.console.addPluginAllowingSleep(plugin.getName(), value); } + + // Purpur start - Bring back server name + @Override + public String getServerName() { + return this.getProperties().serverName; + } + // Purpur end - Bring back server name + + // Purpur start - Lagging threshold + @Override + public boolean isLagging() { + return getServer().lagging; + } + // Purpur end - Lagging threshold } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index aae378697b2f2e388d2a5dfaca24c9197b8abf3e..0ea7df63498492e320f98ddec5bdf33b203cc770 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -2352,6 +2352,50 @@ public class CraftWorld extends CraftRegionAccessor implements World { return (this.getHandle().getDragonFight() == null) ? null : new CraftDragonBattle(this.getHandle().getDragonFight()); } + // Purpur start - Add local difficulty api + public float getLocalDifficultyAt(Location location) { + return getHandle().getCurrentDifficultyAt(org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(location)).getEffectiveDifficulty(); + } + // Purpur end - Add local difficulty api + + // Purpur start - Debug Marker API + @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(), org.bukkit.craftbukkit.util.CraftLocation.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 - Debug Marker API + @Override public Collection getStructures(int x, int z) { return this.getStructures(x, z, struct -> true); diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java index e89c906dc07e52ea8d661e005d8531128ce4d080..4f64d377b56481fe5e1ff82b424fe06cdf8304e8 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -165,6 +165,14 @@ public class Main { .defaultsTo(new File[] {}) .describedAs("Jar file"); + // Purpur start - Purpur config files + acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("purpur.yml")) + .describedAs("Yml file"); + // Purpur end - Purpur config files + acceptsAll(asList("server-name"), "Name of the server") .withRequiredArg() .ofType(String.class) @@ -224,7 +232,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 - Disable outdated build check 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/block/CraftBeehive.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java index ed4692090a115d3d0a0826b10f77e903058eeec6..c5971aadd443dc0ffa26f1740552cc009d244a3e 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java @@ -16,8 +16,15 @@ import org.bukkit.entity.Bee; public class CraftBeehive extends CraftBlockEntityState implements Beehive { + private final List> storage = new ArrayList<>(); // Purpur - Stored Bee API + public CraftBeehive(World world, BeehiveBlockEntity blockEntity) { super(world, blockEntity); + // Purpur start - load bees to be able to modify them individually - Stored Bee API + for(BeehiveBlockEntity.BeeData data : blockEntity.getStored()) { + storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(data, this)); + } + // Purpur end - Stored Bee API } protected CraftBeehive(CraftBeehive state, Location location) { @@ -76,14 +83,54 @@ public class CraftBeehive extends CraftBlockEntityState impl } } + storage.clear(); // Purpur - Stored Bee API return bees; } + // Purpur start - Stored Bee API + @Override + public Bee releaseEntity(org.purpurmc.purpur.entity.StoredEntity entity) { + ensureNoWorldGeneration(); + + if(!getEntities().contains(entity)) { + return null; + } + + if(isPlaced()) { + BeehiveBlockEntity beehive = ((BeehiveBlockEntity) this.getBlockEntityFromWorld()); + BeehiveBlockEntity.BeeData data = ((org.purpurmc.purpur.entity.PurpurStoredBee) entity).getHandle(); + + List list = beehive.releaseBee(getHandle(), data, BeeReleaseStatus.BEE_RELEASED, true); + + if (list.size() == 1) { + storage.remove(entity); + + return (Bee) list.get(0).getBukkitEntity(); + } + } + + return null; + } + + @Override + public List> getEntities() { + return new ArrayList<>(storage); + } + // Purpur end - Stored Bee API + @Override public void addEntity(Bee entity) { Preconditions.checkArgument(entity != null, "Entity must not be null"); + int length = this.getSnapshot().getStored().size(); // Purpur - Stored Bee API this.getSnapshot().addOccupant(((CraftBee) entity).getHandle()); + + // Purpur start - check if new bee was added, and if yes, add to stored bees - Stored Bee API + List storedBeeData = this.getSnapshot().getStored(); + if(length < storedBeeData.size()) { + storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(storedBeeData.getLast(), this)); + } + // Purpur end - Stored Bee API } @Override @@ -100,6 +147,7 @@ public class CraftBeehive extends CraftBlockEntityState impl @Override public void clearEntities() { getSnapshot().clearBees(); + storage.clear(); // Purpur - Stored Bee API } // Paper end } diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java index b7352ab040c979b42ec7efd301bd9d7bfe08129f..79c59d77068709e42b3a908080d147b94fcee209 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java @@ -73,7 +73,7 @@ public class CraftConduit extends CraftBlockEntityState impl public int getRange() { this.ensureNoWorldGeneration(); ConduitBlockEntity conduit = (ConduitBlockEntity) this.getBlockEntityFromWorld(); - return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks) : 0; + return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks, this.world.getHandle()) : 0; // Purpur - Conduit behavior configuration } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java index 6e2f931b06d4ef702e0abf609a5f5bba780c5b7a..15ae2653c1809041b7ab7cb67015e82c226e9f77 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 - Rebrand + String[] parts = message.split("\n"); + for (String part : parts) { + this.sendRawMessage(part); + } + // Purpur end - Rebrand } @Override @@ -88,7 +93,7 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co @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 - Rebrand } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java index 000c5ea5d39d5ab6e5592012ab1cc7ee547505dc..140da7078217f833f490d88faaf803ab1763115d 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java @@ -16,12 +16,12 @@ public class CraftEndermite extends CraftMonster implements Endermite { @Override public boolean isPlayerSpawned() { - return false; + return getHandle().isPlayerSpawned(); // Purpur - Add back player spawned endermite API } @Override public void setPlayerSpawned(boolean playerSpawned) { - // Nop + getHandle().setPlayerSpawned(playerSpawned); // Purpur - Add back player spawned endermite API } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 3b41a37f852c3b494b158e447eade9b030f26556..395951c968e95adffa97e1851d104faf192744fd 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -100,6 +100,25 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { this.entityType = CraftEntityType.minecraftToBukkit(entity.getType()); } + // Purpur start - Fire Immunity API + @Override + public boolean isImmuneToFire() { + return getHandle().fireImmune(); + } + + @Override + public void setImmuneToFire(Boolean fireImmune) { + getHandle().immuneToFire = fireImmune; + } + // Purpur end - Fire Immunity API + + // Purpur start - API for any mob to burn daylight + @Override + public boolean isInDaylight() { + return getHandle().isSunBurnTick(); + } + // Purpur end - API for any mob to burn daylight + public static CraftEntity getEntity(CraftServer server, T entity) { Preconditions.checkArgument(entity != null, "Unknown entity"); @@ -289,6 +308,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { boolean retainPassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS); // Don't allow teleporting between worlds while keeping passengers if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) { + if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur - Add EntityTeleportHinderedEvent return false; } @@ -1313,4 +1333,26 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return this.entity.get(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type)) != null; } + // Purpur start - Ridables + @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(); + } + // Purpur end - Ridables } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java index 934eb5c668b09d871c62a17c8d0916723fa25695..d4e36d6d2e11d43db4c80bf95d0a935f99de576e 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -295,6 +295,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { @Override public void recalculatePermissions() { this.perm.recalculatePermissions(); + getHandle().canPortalInstant = hasPermission("purpur.portal.instant"); // Purpur - Add portal permission bypass } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java index 2d7be6f2afe32fd7555640989313052d37973e61..5bafbb876e4780ff6ea0408c95007bdfcef8fa89 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java @@ -22,4 +22,17 @@ public class CraftIronGolem extends CraftGolem implements IronGolem { public void setPlayerCreated(boolean playerCreated) { this.getHandle().setPlayerCreated(playerCreated); } + + // Purpur start - Summoner API + @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 - Summoner API } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java index ca656d97a00eef86773be9833fd908283002c885..01759ae999cd158e540f1e282473740758d6cf54 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java @@ -143,4 +143,53 @@ public class CraftItem extends CraftEntity implements Item { public UUID getThrower() { return this.getHandle().thrower; } + + // Purpur start - Item entity immunities + @Override + public void setImmuneToCactus(boolean immuneToCactus) { + this.getHandle().immuneToCactus = immuneToCactus; + } + + @Override + public boolean isImmuneToCactus() { + return this.getHandle().immuneToCactus; + } + + @Override + public void setImmuneToExplosion(boolean immuneToExplosion) { + this.getHandle().immuneToExplosion = immuneToExplosion; + } + + @Override + public boolean isImmuneToExplosion() { + return this.getHandle().immuneToExplosion; + } + + // Purpur start - Fire Immunity API + @Override + public void setImmuneToFire(@org.jetbrains.annotations.Nullable Boolean immuneToFire) { + this.getHandle().immuneToFire = (immuneToFire != null && immuneToFire); + } + // Purpur end - Fire Immunity API + + @Override + public void setImmuneToFire(boolean immuneToFire) { + this.setImmuneToFire((Boolean) immuneToFire); // Purpur - Fire Immunity API + } + + @Override + public boolean isImmuneToFire() { + return this.getHandle().immuneToFire; + } + + @Override + public void setImmuneToLightning(boolean immuneToLightning) { + this.getHandle().immuneToLightning = immuneToLightning; + } + + @Override + public boolean isImmuneToLightning() { + return this.getHandle().immuneToLightning; + } + // Purpur end - Item entity immunities } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java index 6b4e74b808b4fb6ac0143d5c82571657b2c49683..fc4fa99a993a017676da2be3cb254399d421bce1 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -514,7 +514,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { net.minecraft.server.level.ServerPlayer nmsKiller = killer == null ? null : ((CraftPlayer) killer).getHandle(); this.getHandle().setLastHurtByMob(nmsKiller); if (nmsKiller != null) { - this.getHandle().setLastHurtByPlayer(nmsKiller, 100); // value taken from LivingEntity#resolvePlayerResponsibleForDamage + this.getHandle().setLastHurtByPlayer(nmsKiller, this.getHandle().level().purpurConfig.mobLastHurtByPlayerTime); // value taken from LivingEntity#resolvePlayerResponsibleForDamage // Purpur - Config for mob last hurt by player time } else { this.getHandle().lastHurtByPlayer = null; this.getHandle().lastHurtByPlayerMemoryTime = 0; @@ -1167,4 +1167,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { public CombatTracker getCombatTracker() { return this.getHandle().getCombatTracker().paperCombatTracker; } + + // Purpur start - API for any mob to burn daylight + @Override + public boolean shouldBurnInDay() { + return this.getHandle().shouldBurnInDay(); + } + + @Override + public void setShouldBurnInDay(final boolean shouldBurnInDay) { + this.getHandle().setShouldBurnInDay(shouldBurnInDay); + } + // Purpur end - API for any mob to burn daylight } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java index 02deb9c37250144be9afa0fe55df2a49017a7070..dfb597fcb8d01ed6cf20eae57e260949356275de 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java @@ -86,4 +86,17 @@ public class CraftLlama extends CraftChestedHorse implements Llama, com.destroys public Llama getCaravanTail() { return this.getHandle().caravanTail == null ? null : (Llama) this.getHandle().caravanTail.getBukkitEntity(); } + // Paper end + + // Purpur start - Llama API + @Override + public boolean shouldJoinCaravan() { + return getHandle().shouldJoinCaravan; + } + + @Override + public void setShouldJoinCaravan(boolean shouldJoinCaravan) { + getHandle().shouldJoinCaravan = shouldJoinCaravan; + } + // Purpur end - Llama API } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 1db6276ae7a547860cf2c6f85b37ec4d6b33655a..fcc7bc9098e2544588d8b36dd0cff59e6a2563bd 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -614,10 +614,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public void setPlayerListName(String name) { + // Purpur start - AFK API + setPlayerListName(name, false); + } + public void setPlayerListName(String name, boolean useMM) { + // Purpur end - AFK API if (name == null) { name = this.getName(); } - this.getHandle().listName = name.equals(this.getName()) ? null : CraftChatMessage.fromStringOrNull(name); + this.getHandle().listName = name.equals(this.getName()) ? null : useMM ? io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name)) : CraftChatMessage.fromStringOrNull(name); // Purpur - AFK API if (this.getHandle().connection == null) return; // Paper - Updates are possible before the player has fully joined for (ServerPlayer player : (List) this.server.getHandle().players) { if (player.getBukkitEntity().canSee(this)) { @@ -1431,6 +1436,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { // Paper start - Teleport passenger API // Don't allow teleporting between worlds while keeping passengers if (ignorePassengers && entity.isVehicle() && location.getWorld() != this.getWorld()) { + if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur - Add EntityTeleportHinderedEvent return false; } @@ -1452,6 +1458,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { } if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API + if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur - Add EntityTeleportHinderedEvent return false; } @@ -2723,6 +2730,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { return this.getHandle().getAbilities().walkingSpeed * 2f; } + // Purpur start - OfflinePlayer API + @Override + public boolean teleportOffline(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 teleportOfflineAsync(Location destination) { + return this.teleportAsync(destination); + } + + @Override + public java.util.concurrent.CompletableFuture teleportOfflineAsync(Location destination, PlayerTeleportEvent.TeleportCause cause) { + return this.teleportAsync(destination, cause); + } + // Purpur end - OfflinePlayer API + private void validateSpeed(float value) { Preconditions.checkArgument(value <= 1f && value >= -1f, "Speed value (%s) need to be between -1f and 1f", value); } @@ -3576,4 +3605,76 @@ public class CraftPlayer extends CraftHumanEntity implements Player { public void setDeathScreenScore(final int score) { getHandle().setScore(score); } + + // Purpur start - Purpur client support + @Override + public boolean usesPurpurClient() { + return getHandle().purpurClient; + } + // Purpur end - Purpur client support + + // Purpur start - AFK API + @Override + public boolean isAfk() { + return getHandle().isAfk(); + } + + @Override + public void setAfk(boolean setAfk) { + getHandle().setAfk(setAfk); + } + + @Override + public void resetIdleTimer() { + getHandle().resetLastActionTime(); + } + // Purpur end - AFK API + + // Purpur start - Debug Marker API + @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; + this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestAddMarkerDebugPayload(org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(location), argb, text, 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() { + if (this.getHandle().connection == null) return; + this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestClearMarkersDebugPayload())); + } + // Purpur end - Debug Marker API + + // Purpur start - Death screen API + @Override + public void sendDeathScreen(net.kyori.adventure.text.Component message) { + if (this.getHandle().connection == null) return; + this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), io.papermc.paper.adventure.PaperAdventure.asVanilla(message))); + } + // Purpur end - Death screen API } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java index f03f9f94c51ef2f2eb24e4f65ec69b542b1c0a07..af60e3ef00ce1e98708d9963d466e72b6cde53cc 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java @@ -24,4 +24,17 @@ public class CraftSnowman extends CraftGolem implements Snowman, com.destroystok public void setDerp(boolean derpMode) { this.getHandle().setPumpkin(!derpMode); } + + // Purpur start - Summoner API + @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 - Summoner API } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java index 66f0089ee33c36b9d13fb0b87303d33f08021ed1..26f056294e19f509bfd4986b9650e13f92a8feaf 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java @@ -258,4 +258,11 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { public void restock() { getHandle().restock(); } + + // Purpur start - Lobotomize stuck villagers + @Override + public boolean isLobotomized() { + return getHandle().isLobotomized(); + } + // Purpur end - Lobotomize stuck villagers } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java index 2e11df97e5c8ff5fbf22ba87946d6018ac8bbbed..83b779eb0adcaf2e6d5658cddc2607f6deea6bb9 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java @@ -92,4 +92,18 @@ public class CraftWither extends CraftMonster implements Wither, com.destroystok public void enterInvulnerabilityPhase() { this.getHandle().makeInvulnerable(); } + // Paper end + + // Purpur start - Summoner API + @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 - Summoner API } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java index 33866c5a5591150de4c1d8f4c175122db3bd5bd4..5fd656f43985e3972c9f572d5b05bf5c81da2678 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java @@ -135,4 +135,15 @@ public class CraftWolf extends CraftTameableAnimal implements Wolf { super(holder); } } + // Purpur start - Configurable chance for wolves to spawn rabid + @Override + public boolean isRabid() { + return getHandle().isRabid(); + } + + @Override + public void setRabid(boolean isRabid) { + getHandle().setRabid(isRabid); + } + // Purpur end - Configurable chance for wolves to spawn rabid } diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 9774c9c72a910ccd919b903d92f4068c06d199d7..5300a513a295d472752d31a6e8af48bb64b06704 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -514,6 +514,15 @@ public class CraftEventFactory { } craftServer.getPluginManager().callEvent(event); + // Purpur start - Ridables + if (player != null) { + switch (action) { + case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> player.processClick(InteractionHand.MAIN_HAND); + case RIGHT_CLICK_BLOCK, RIGHT_CLICK_AIR -> player.processClick(InteractionHand.OFF_HAND); + } + } + // Purpur end - Ridables + return event; } @@ -1046,6 +1055,7 @@ public class CraftEventFactory { EntityDamageEvent event; if (damager != null) { event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions, critical); + damager.processClick(InteractionHand.MAIN_HAND); // Purpur - Ridables } else { event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, bukkitDamageSource, 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 c00ddfe41439954fa0fd87c0933f274c8a752eb6..5fde4c71915f55f100cd30406803d6fca5b573dc 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java @@ -150,8 +150,19 @@ public class CraftContainer extends AbstractContainerMenu { case PLAYER: case CHEST: case ENDER_CHEST: + // Purpur start - Barrels and enderchests 6 rows + this.delegate = new ChestMenu(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? net.minecraft.world.inventory.MenuType.GENERIC_9x6 : net.minecraft.world.inventory.MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9); + break; case BARREL: - this.delegate = new ChestMenu(net.minecraft.world.inventory.MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9); + this.delegate = new ChestMenu(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { + case 6 -> net.minecraft.world.inventory.MenuType.GENERIC_9x6; + case 5 -> net.minecraft.world.inventory.MenuType.GENERIC_9x5; + case 4 -> net.minecraft.world.inventory.MenuType.GENERIC_9x4; + case 2 -> net.minecraft.world.inventory.MenuType.GENERIC_9x2; + case 1 -> net.minecraft.world.inventory.MenuType.GENERIC_9x1; + default -> net.minecraft.world.inventory.MenuType.GENERIC_9x3; + }, windowId, bottom, top, top.getContainerSize() / 9); + // Purpur end - Barrels and enderchests 6 rows 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 f850e6cea92edc87ed54cf54488b5ebb606913ed..19180c08f41db939c1a9f0caeb62e5beb1117f69 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java @@ -84,7 +84,7 @@ public class CraftInventory implements Inventory { @Override public void setContents(ItemStack[] items) { - Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize()); + // Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize()); // Purpur - Barrels and enderchests 6 rows for (int i = 0; i < this.getSize(); i++) { if (i >= items.length) { diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java index 792cb6adf0c7a6335cc5985fce8bed2e0f1149af..5734c5caffda79383ae30df20c3defb51b87f39e 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java @@ -19,6 +19,10 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn private int repairCost; private int repairCostAmount; private int maximumRepairCost; + // Purpur start - Anvil API + private boolean bypassCost; + private boolean canDoUnsafeEnchants; + // Purpur end - Anvil API public CraftInventoryAnvil(Location location, Container inventory, Container resultInventory) { super(inventory, resultInventory); @@ -27,6 +31,10 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn this.repairCost = CraftInventoryAnvil.DEFAULT_REPAIR_COST; this.repairCostAmount = CraftInventoryAnvil.DEFAULT_REPAIR_COST_AMOUNT; this.maximumRepairCost = CraftInventoryAnvil.DEFAULT_MAXIMUM_REPAIR_COST; + // Purpur start - Anvil API + this.bypassCost = false; + this.canDoUnsafeEnchants = false; + // Purpur end - Anvil API } @Override @@ -113,4 +121,30 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn consumer.accept(cav); } } + + // Purpur start - Anvil API + @Override + public boolean canBypassCost() { + this.syncWithArbitraryViewValue((cav) -> this.bypassCost = cav.canBypassCost()); + return this.bypassCost; + } + + @Override + public void setBypassCost(boolean bypassCost) { + this.bypassCost = bypassCost; + this.syncViews((cav) -> cav.setBypassCost(bypassCost)); + } + + @Override + public boolean canDoUnsafeEnchants() { + this.syncWithArbitraryViewValue((cav) -> this.canDoUnsafeEnchants = cav.canDoUnsafeEnchants()); + return this.canDoUnsafeEnchants; + } + + @Override + public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) { + this.canDoUnsafeEnchants = canDoUnsafeEnchants; + this.syncViews((cav) -> cav.setDoUnsafeEnchants(canDoUnsafeEnchants)); + } + // Purpur end - Anvil API } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 4a7b4b603c2c555c320911e618a4392628c2c754..0a3ad330e1d40cbe9e03fc3585ee309fe9f5c930 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -667,4 +667,285 @@ public final class CraftItemStack extends ItemStack { } // Paper end - data component API + + // Purpur start - ItemStack convenience methods + @Override + public String getDisplayName() { + return getItemMeta().getDisplayName(); + } + + @Override + public void setDisplayName(String name) { + ItemMeta itemMeta = getItemMeta(); + itemMeta.setDisplayName(name); + setItemMeta(itemMeta); + } + + @Override + public boolean hasDisplayName() { + return hasItemMeta() && getItemMeta().hasDisplayName(); + } + + @Override + public String getLocalizedName() { + return getItemMeta().getLocalizedName(); + } + + @Override + public void setLocalizedName(String name) { + ItemMeta itemMeta = getItemMeta(); + itemMeta.setLocalizedName(name); + setItemMeta(itemMeta); + } + + @Override + public boolean hasLocalizedName() { + return hasItemMeta() && getItemMeta().hasLocalizedName(); + } + + @Override + public boolean hasLore() { + return hasItemMeta() && getItemMeta().hasLore(); + } + + @Override + public boolean hasEnchant(Enchantment ench) { + return hasItemMeta() && getItemMeta().hasEnchant(ench); + } + + @Override + public int getEnchantLevel(Enchantment ench) { + return getItemMeta().getEnchantLevel(ench); + } + + @Override + public Map getEnchants() { + return getItemMeta().getEnchants(); + } + + @Override + public boolean addEnchant(Enchantment ench, int level, boolean ignoreLevelRestriction) { + ItemMeta itemMeta = getItemMeta(); + boolean result = itemMeta.addEnchant(ench, level, ignoreLevelRestriction); + setItemMeta(itemMeta); + return result; + } + + @Override + public boolean removeEnchant(Enchantment ench) { + ItemMeta itemMeta = getItemMeta(); + boolean result = itemMeta.removeEnchant(ench); + setItemMeta(itemMeta); + return result; + } + + @Override + public boolean hasEnchants() { + return hasItemMeta() && getItemMeta().hasEnchants(); + } + + @Override + public boolean hasConflictingEnchant(Enchantment ench) { + return hasItemMeta() && getItemMeta().hasConflictingEnchant(ench); + } + + @Override + public void setCustomModelData(Integer data) { + ItemMeta itemMeta = getItemMeta(); + itemMeta.setCustomModelData(data); + setItemMeta(itemMeta); + } + + @Override + public int getCustomModelData() { + return getItemMeta().getCustomModelData(); + } + + @Override + public boolean hasCustomModelData() { + return hasItemMeta() && getItemMeta().hasCustomModelData(); + } + + @Override + public boolean hasBlockData() { + return hasItemMeta() && ((org.bukkit.inventory.meta.BlockDataMeta) getItemMeta()).hasBlockData(); + } + + @Override + public org.bukkit.block.data.BlockData getBlockData(Material material) { + return ((org.bukkit.inventory.meta.BlockDataMeta) getItemMeta()).getBlockData(material); + } + + @Override + public void setBlockData(org.bukkit.block.data.BlockData blockData) { + ItemMeta itemMeta = getItemMeta(); + ((org.bukkit.inventory.meta.BlockDataMeta) itemMeta).setBlockData(blockData); + setItemMeta(itemMeta); + } + + @Override + public int getRepairCost() { + return ((org.bukkit.inventory.meta.Repairable) getItemMeta()).getRepairCost(); + } + + @Override + public void setRepairCost(int cost) { + ItemMeta itemMeta = getItemMeta(); + ((org.bukkit.inventory.meta.Repairable) itemMeta).setRepairCost(cost); + setItemMeta(itemMeta); + } + + @Override + public boolean hasRepairCost() { + return hasItemMeta() && ((org.bukkit.inventory.meta.Repairable) getItemMeta()).hasRepairCost(); + } + + @Override + public boolean isUnbreakable() { + return hasItemMeta() && getItemMeta().isUnbreakable(); + } + + @Override + public void setUnbreakable(boolean unbreakable) { + ItemMeta itemMeta = getItemMeta(); + itemMeta.setUnbreakable(unbreakable); + setItemMeta(itemMeta); + } + + @Override + public boolean hasAttributeModifiers() { + return hasItemMeta() && getItemMeta().hasAttributeModifiers(); + } + + @Override + public com.google.common.collect.Multimap getAttributeModifiers() { + return getItemMeta().getAttributeModifiers(); + } + + @Override + public com.google.common.collect.Multimap getAttributeModifiers(org.bukkit.inventory.EquipmentSlot slot) { + return getItemMeta().getAttributeModifiers(slot); + } + + @Override + public java.util.Collection getAttributeModifiers(org.bukkit.attribute.Attribute attribute) { + return getItemMeta().getAttributeModifiers(attribute); + } + + @Override + public boolean addAttributeModifier(org.bukkit.attribute.Attribute attribute, org.bukkit.attribute.AttributeModifier modifier) { + ItemMeta itemMeta = getItemMeta(); + boolean result = itemMeta.addAttributeModifier(attribute, modifier); + setItemMeta(itemMeta); + return result; + } + + @Override + public void setAttributeModifiers(com.google.common.collect.Multimap attributeModifiers) { + ItemMeta itemMeta = getItemMeta(); + itemMeta.setAttributeModifiers(attributeModifiers); + setItemMeta(itemMeta); + } + + @Override + public boolean removeAttributeModifier(org.bukkit.attribute.Attribute attribute) { + ItemMeta itemMeta = getItemMeta(); + boolean result = itemMeta.removeAttributeModifier(attribute); + setItemMeta(itemMeta); + return result; + } + + @Override + public boolean removeAttributeModifier(org.bukkit.inventory.EquipmentSlot slot) { + ItemMeta itemMeta = getItemMeta(); + boolean result = itemMeta.removeAttributeModifier(slot); + setItemMeta(itemMeta); + return result; + } + + @Override + public boolean removeAttributeModifier(org.bukkit.attribute.Attribute attribute, org.bukkit.attribute.AttributeModifier modifier) { + ItemMeta itemMeta = getItemMeta(); + boolean result = itemMeta.removeAttributeModifier(attribute, modifier); + setItemMeta(itemMeta); + return result; + } + + @Override + public boolean hasDamage() { + return hasItemMeta() && ((org.bukkit.inventory.meta.Damageable) getItemMeta()).hasDamage(); + } + + @Override + public int getDamage() { + return ((org.bukkit.inventory.meta.Damageable) getItemMeta()).getDamage(); + } + + @Override + public void setDamage(int damage) { + ItemMeta itemMeta = getItemMeta(); + ((org.bukkit.inventory.meta.Damageable) itemMeta).setDamage(damage); + setItemMeta(itemMeta); + } + + @Override + public void repair() { + repair(1); + } + + @Override + public boolean damage() { + return damage(1); + } + + @Override + public void repair(int amount) { + damage(-amount); + } + + @Override + public boolean damage(int amount) { + return damage(amount, false); + } + + @Override + public boolean damage(int amount, boolean ignoreUnbreaking) { + org.bukkit.inventory.meta.Damageable damageable = (org.bukkit.inventory.meta.Damageable) getItemMeta(); + if (amount > 0) { + int unbreaking = getEnchantLevel(Enchantment.UNBREAKING); + int reduce = 0; + for (int i = 0; unbreaking > 0 && i < amount; ++i) { + if (reduceDamage(java.util.concurrent.ThreadLocalRandom.current(), unbreaking)) { + ++reduce; + } + } + amount -= reduce; + if (amount <= 0) { + return isBroke(damageable.getDamage()); + } + } + int damage = damageable.getDamage() + amount; + damageable.setDamage(damage); + setItemMeta((ItemMeta) damageable); + return isBroke(damage); + } + + private boolean isBroke(int damage) { + if (damage > getType().getMaxDurability()) { + if (getAmount() > 0) { + // ensure it "breaks" + setAmount(0); + } + return true; + } + return false; + } + + private boolean reduceDamage(java.util.Random random, int unbreaking) { + if (getType().isArmor()) { + return random.nextFloat() < 0.6F; + } + return random.nextInt(unbreaking + 1) > 0; + } + // Purpur end - ItemStack convenience methods } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java index 4864e2016cb1d377425297fd1c52b383632cb59e..953d64d128a53eb9ec2ff55140dde5cb80326044 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java @@ -36,6 +36,7 @@ public interface CraftRecipe extends Recipe { stack = Ingredient.of(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map((mat) -> CraftItemType.bukkitToMinecraft(mat))); } else if (bukkit instanceof RecipeChoice.ExactChoice) { stack = Ingredient.ofStacks(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> CraftItemStack.asNMSCopy(mat)).toList()); + stack.predicate = ((RecipeChoice.ExactChoice) bukkit).getPredicate(); // Purpur - Add predicate to recipe's ExactChoice ingredient // Paper start - support "empty" choices - legacy method that spigot might incorrectly call // Their impl of Ingredient.of() will error, ingredients need at least one entry. // Callers running into this exception may have passed an incorrect empty() recipe choice to a non-empty slot or diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java index 2b3844f5ecc1bfbf3f5dab1c00d06f8be27f39e9..4e535c287137ceedd4d48047b03f4578c33467b0 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java @@ -73,4 +73,26 @@ public class CraftAnvilView extends CraftInventoryView experience 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/resources/log4j2.xml b/src/main/resources/log4j2.xml index d2a75850af9c6ad2aca66a5f994f1b587d73eac4..a056aa167887abef9e6d531a9edd2cda433567d2 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -2,7 +2,16 @@ - + + + + + + + + + diff --git a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java index fe08e446e86d53cef6eecc33cd484e93adc42871..54fff448fded12cd6110583f0c255f2ca86388da 100644 --- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java +++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java @@ -46,6 +46,7 @@ public class MinecraftCommandPermissionsTest { Set foundPerms = new HashSet<>(); for (CommandNode child : root.getChildren()) { final String vanillaPerm = VanillaCommandWrapper.getPermission(child); + if (TO_SKIP.contains(vanillaPerm)) continue; // Purpur - Skip junit tests for purpur commands if (!perms.contains(vanillaPerm)) { missing.add("Missing permission for " + child.getName() + " (" + vanillaPerm + ") command"); } else { @@ -58,6 +59,25 @@ public class MinecraftCommandPermissionsTest { } private static final List TO_SKIP = List.of( + // Purpur start - Skip junit tests for purpur commands + "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 - Skip junit tests for purpur commands "minecraft.command.selector" );