apply paper server file patches

This commit is contained in:
granny
2025-11-25 21:41:09 -08:00
parent 56a26034de
commit e6561c98ae
45 changed files with 111 additions and 458 deletions

View File

@@ -0,0 +1,30 @@
--- a/src/main/java/com/destroystokyo/paper/Metrics.java
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
@@ -593,7 +_,7 @@
boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
// Only start Metrics, if it's enabled in the config
if (config.getBoolean("enabled", true)) {
- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger());
+ Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Purpur - Purpur config files
metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
String minecraftVersion = Bukkit.getVersion();
@@ -602,16 +_,8 @@
}));
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-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash);
- } else {
- paperVersion = "unknown";
- }
- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion));
+ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur - Purpur config files
+ metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // Purpur - Purpur config files
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>();

View File

@@ -0,0 +1,78 @@
--- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
@@ -35,7 +_,10 @@
private static final Logger LOGGER = LogUtils.getClassLogger();
private static final int DISTANCE_ERROR = -1;
private static final int DISTANCE_UNKNOWN = -2;
- private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads/paper";
+ // Purpur start - Rebrand
+ private static final String DOWNLOAD_PAGE = "https://purpurmc.org/downloads";
+ private static int distance = DISTANCE_UNKNOWN; public int distance() { return distance; }
+ // Purpur end - Rebrand
@Override
public long getCacheTime() {
@@ -49,7 +_,7 @@
if (build.buildNumber().isEmpty() && build.gitCommit().isEmpty()) {
updateMessage = text("You are running a development version without access to version information", color(0xFF5300));
} else {
- updateMessage = getUpdateStatusMessage("PaperMC/Paper", build);
+ updateMessage = getUpdateStatusMessage("PurpurMC/Purpur", build); // Purpur - Rebrand
}
final @Nullable Component history = this.getHistory();
@@ -57,7 +_,7 @@
}
private static Component getUpdateStatusMessage(final String repo, final ServerBuildInfo build) {
- int distance = DISTANCE_ERROR;
+ //int distance = DISTANCE_ERROR; // Purpur - use field - Rebrand
final OptionalInt buildNumber = build.buildNumber();
if (buildNumber.isPresent()) {
@@ -71,10 +_,10 @@
}
return switch (distance) {
- case DISTANCE_ERROR -> text("Error obtaining version information", NamedTextColor.YELLOW);
- case 0 -> text("You are running the latest version", NamedTextColor.GREEN);
- case DISTANCE_UNKNOWN -> text("Unknown version", NamedTextColor.YELLOW);
- default -> text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW)
+ case DISTANCE_ERROR -> text("* Error obtaining version information", NamedTextColor.RED); // Purpur - Rebrand
+ case 0 -> text("* You are running the latest version", NamedTextColor.GREEN); // Purpur - Rebrand
+ case DISTANCE_UNKNOWN -> text("* Unknown version", NamedTextColor.YELLOW); // Purpur - Rebrand
+ default -> text("* You are " + distance + " version(s) behind", NamedTextColor.YELLOW) // Purpur - Rebrand
.append(Component.newline())
.append(text("Download the new version at: ")
.append(text(DOWNLOAD_PAGE, NamedTextColor.GOLD)
@@ -86,18 +_,15 @@
private static int fetchDistanceFromSiteApi(final ServerBuildInfo build, final int jenkinsBuild) {
try {
try (final BufferedReader reader = Resources.asCharSource(
- URI.create("https://api.papermc.io/v2/projects/paper/versions/" + build.minecraftVersionId()).toURL(),
+ URI.create("https://api.purpurmc.org/v2/purpur/" + build.minecraftVersionId()).toURL(), // Purpur - Rebrand
StandardCharsets.UTF_8
).openBufferedStream()) {
final JsonObject json = new Gson().fromJson(reader, JsonObject.class);
- final JsonArray builds = json.getAsJsonArray("builds");
- final int latest = StreamSupport.stream(builds.spliterator(), false)
- .mapToInt(JsonElement::getAsInt)
- .max()
- .orElseThrow();
+ //final JsonArray builds = json.getAsJsonArray("builds"); // Purpur - Rebrand
+ final int latest = json.getAsJsonObject("builds").getAsJsonPrimitive("latest").getAsInt(); // Purpur - Rebrand
return latest - jenkinsBuild;
} catch (final JsonSyntaxException ex) {
- LOGGER.error("Error parsing json from Paper's downloads API", ex);
+ LOGGER.error("Error parsing json from Purpur's downloads API", ex); // Purpur - Rebrand
return DISTANCE_ERROR;
}
} catch (final IOException e) {
@@ -141,6 +_,6 @@
return null;
}
- return text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC);
+ return text("Previous: " + oldVersion, NamedTextColor.GRAY); // Purpur - Rebrand
}
}

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
+++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
@@ -20,7 +_,7 @@
@Override
protected LineReader buildReader(LineReaderBuilder builder) {
builder
- .appName("Paper")
+ .appName("Purpur") // Purpur - Rebrand
.variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history"))
.completer(new ConsoleCommandCompleter(this.server))
.option(LineReader.Option.COMPLETE_IN_WORD, true);

View File

@@ -0,0 +1,14 @@
--- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
@@ -148,6 +_,11 @@
private static final Map<String, String> RENAMES = Util.make(new HashMap<>(), map -> {
map.put("AbstractSkeleton$1", "AbstractSkeletonMelee");
+ // Purpur start - Add option to disable zombie aggressiveness towards villagers
+ map.put("Zombie$1", "ZombieAttackVillager");
+ map.put("Drowned$1", "DrownedAttackVillager");
+ // Purpur end - Add option to disable zombie aggressiveness towards villagers
+
// remove duplicate
map.put("TraderLlama$TraderLlamaDefendWanderingTraderGoal", "TraderLlamaDefendWanderingTraderGoal");
map.put("AbstractIllager$RaiderOpenDoorGoal", "RaiderOpenDoorGoal");

View File

@@ -0,0 +1,20 @@
--- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java
+++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java
@@ -60,7 +_,7 @@
Vector<String> vector = new Vector<>();
// Follows CraftServer#getTPS
- double[] tps = server.getTPS();
+ double[] tps = server.getTPS(); // Purpur - diff on change
String[] tpsAvg = new String[tps.length];
for ( int g = 0; g < tps.length; g++) {
@@ -69,7 +_,7 @@
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((double)this.server.getAverageTickTimeNanos() / (double) TimeUtil.NANOSECONDS_PER_MILLISECOND) + " 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 - Add 5 second tps average in /tps
setListData(vector);
}

View File

@@ -0,0 +1,31 @@
--- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java
+++ b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java
@@ -31,6 +_,7 @@
private static final String ATTRIBUTE_GIT_COMMIT = "Git-Commit";
private static final String BRAND_PAPER_NAME = "Paper";
+ private static final String BRAND_PURPUR_NAME = "Purpur"; // Purpur - Rebrand
private static final String BUILD_DEV = "DEV";
@@ -42,9 +_,9 @@
this(
getManifestAttribute(manifest, ATTRIBUTE_BRAND_ID)
.map(Key::key)
- .orElse(BRAND_PAPER_ID),
+ .orElse(BRAND_PURPUR_ID), // Purpur - Rebrand
getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME)
- .orElse(BRAND_PAPER_NAME),
+ .orElse(BRAND_PURPUR_NAME), // Purpur - Rebrand
SharedConstants.getCurrentVersion().id(),
SharedConstants.getCurrentVersion().name(),
getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER)
@@ -61,7 +_,7 @@
@Override
public boolean isBrandCompatible(final @NotNull Key brandId) {
- return brandId.equals(this.brandId);
+ return brandId.equals(this.brandId) || brandId.equals(BRAND_PAPER_ID); // Purpur - Fix pufferfish issues // Purpur - Rebrand
}
@Override

View File

@@ -0,0 +1,125 @@
--- a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
@@ -74,10 +_,10 @@
.build();
}
- private static <T> List<Component> formatProviders(final TreeMap<String, PluginProvider<T>> plugins) {
+ private static <T> List<Component> formatProviders(final TreeMap<String, PluginProvider<T>> plugins, final CommandSender sender) { // Purpur - Improve output of plugins command
final List<Component> components = new ArrayList<>(plugins.size());
for (final PluginProvider<T> entry : plugins.values()) {
- components.add(formatProvider(entry));
+ components.add(formatProvider(entry, sender)); // Purpur - Improve output of plugins command
}
boolean isFirst = true;
@@ -104,15 +_,49 @@
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);
@@ -130,6 +_,23 @@
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<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 - 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")) {
@@ -188,25 +_,25 @@
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);
}

View File

@@ -0,0 +1,14 @@
--- a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
+++ b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
@@ -54,9 +_,9 @@
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) {

View File

@@ -0,0 +1,199 @@
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -342,6 +_,12 @@
@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;
@@ -579,4 +_,183 @@
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<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.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
}

View File

@@ -0,0 +1,146 @@
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -417,6 +_,20 @@
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());
@@ -992,6 +_,7 @@
org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot
this.console.paperConfigurations.reloadConfigs(this.console);
+ 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 && world.getGameRules().get(GameRules.SPAWN_MONSTERS)); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
@@ -1007,6 +_,7 @@
}
}
world.spigotConfig.init(); // Spigot
+ world.purpurConfig.init(); // Purpur - Purpur config files
}
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
@@ -1024,6 +_,7 @@
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");
@@ -1484,6 +_,60 @@
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) -> world.clearBlockHighlights());
+ }
+ // Purpur end - Debug Marker API
+
@Override
public List<Recipe> getRecipesFor(ItemStack result) {
Preconditions.checkArgument(result != null, "ItemStack cannot be null");
@@ -2729,6 +_,18 @@
return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console);
}
+ // 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();
@@ -2964,4 +_,18 @@
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
}

View File

@@ -0,0 +1,55 @@
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -1937,6 +_,52 @@
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) {
+ // Purpur - TODO: NOOP until further notice
+ //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() {
+ // Purpur - TODO: NOOP until further notice
+ //net.minecraft.network.protocol.game.DebugPackets.sendGameTestClearPacket(getHandle());
+ }
+ // Purpur end - Debug Marker API
+
@Override
public Collection<GeneratedStructure> getStructures(int x, int z) {
return this.getStructures(x, z, struct -> true);

View File

@@ -0,0 +1,26 @@
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -164,6 +_,14 @@
.defaultsTo(new File[] {})
.describedAs("Jar file");
+ // Purpur start - Purpur config files
+ this.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
+
this.accepts("server-name", "Name of the server")
.withRequiredArg()
.ofType(String.class)
@@ -223,7 +_,7 @@
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();

View File

@@ -0,0 +1,81 @@
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java
@@ -16,8 +_,15 @@
public class CraftBeehive extends CraftBlockEntityState<BeehiveBlockEntity> implements Beehive {
+ private final List<org.purpurmc.purpur.entity.StoredEntity<Bee>> 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, blockEntity));
+ }
+ // Purpur end - Stored Bee API
}
protected CraftBeehive(CraftBeehive state, Location location) {
@@ -76,14 +_,54 @@
}
}
+ storage.clear(); // Purpur - Stored Bee API
return bees;
}
+ // Purpur start - Stored Bee API
+ @Override
+ public Bee releaseEntity(org.purpurmc.purpur.entity.StoredEntity<Bee> 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<Entity> 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<org.purpurmc.purpur.entity.StoredEntity<Bee>> 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<BeehiveBlockEntity.BeeData> storedBeeData = this.getSnapshot().getStored();
+ if(length < storedBeeData.size()) {
+ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(storedBeeData.getLast(), this, this.getBlockEntity()));
+ }
+ // Purpur end - Stored Bee API
}
@Override
@@ -100,6 +_,7 @@
@Override
public void clearEntities() {
getSnapshot().clearBees();
+ storage.clear(); // Purpur - Stored Bee API
}
// Paper end
}

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java
@@ -75,7 +_,7 @@
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

View File

@@ -0,0 +1,25 @@
--- a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
@@ -21,7 +_,12 @@
@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 +_,7 @@
@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

View File

@@ -0,0 +1,20 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftCopperGolem.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCopperGolem.java
@@ -55,4 +_,17 @@
default -> throw new IllegalStateException("Unexpected value: " + oxidizing);
}
}
+
+ // 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
}

View File

@@ -0,0 +1,17 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java
@@ -16,12 +_,12 @@
@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

View File

@@ -0,0 +1,21 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -114,6 +_,18 @@
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
+
public static <T extends Entity> CraftEntity getEntity(CraftServer server, T entity) {
Preconditions.checkArgument(entity != null, "Unknown entity");

View File

@@ -0,0 +1,10 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
@@ -291,6 +_,7 @@
@Override
public void recalculatePermissions() {
this.perm.recalculatePermissions();
+ getHandle().canPortalInstant = hasPermission("purpur.portal.instant"); // Purpur - Add portal permission bypass
}
@Override

View File

@@ -0,0 +1,20 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java
@@ -22,4 +_,17 @@
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
}

View File

@@ -0,0 +1,56 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
@@ -145,4 +_,53 @@
public UUID getThrower() {
return Optionull.map(this.getHandle().thrower, EntityReference::getUUID);
}
+
+ // 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
}

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -482,7 +_,7 @@
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;

View File

@@ -0,0 +1,20 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java
@@ -86,4 +_,17 @@
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
}

View File

@@ -0,0 +1,129 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -581,10 +_,15 @@
@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 : this.server.getHandle().players) {
if (player.getBukkitEntity().canSee(this)) {
@@ -987,6 +_,80 @@
}
}
+ // Purpur start - Purpur client support
+ @Override
+ public boolean usesPurpurClient() {
+ return getHandle().connection.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;
+ // Purpur - TODO: NOOP until further notice
+ //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;
+ // Purpur - TODO: NOOP until further notice
+ //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
+
@Override
public void sendBlockDamage(Location loc, float progress, org.bukkit.entity.Entity source) {
Preconditions.checkArgument(source != null, "source must not be null");
@@ -2494,6 +_,28 @@
public float getWalkSpeed() {
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, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) {
+ return this.teleport(destination, cause);
+ }
+
+ @Override
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination) {
+ return this.teleportAsync(destination);
+ }
+
+ @Override
+ public java.util.concurrent.CompletableFuture<Boolean> teleportOfflineAsync(Location destination, org.bukkit.event.player.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);

View File

@@ -0,0 +1,20 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java
@@ -24,4 +_,17 @@
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
}

View File

@@ -0,0 +1,14 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
@@ -243,4 +_,11 @@
public void restock() {
getHandle().restock();
}
+
+ // Purpur start - Lobotomize stuck villagers
+ @Override
+ public boolean isLobotomized() {
+ return getHandle().isLobotomized();
+ }
+ // Purpur end - Lobotomize stuck villagers
}

View File

@@ -0,0 +1,21 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java
@@ -92,4 +_,18 @@
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
}

View File

@@ -0,0 +1,21 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java
@@ -90,6 +_,18 @@
this.getHandle().setSoundVariant(CraftSoundVariant.bukkitToMinecraftHolder(soundVariant));
}
+ // 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
+
public static class CraftVariant extends HolderableBase<WolfVariant> implements Variant {
public static Variant minecraftHolderToBukkit(Holder<WolfVariant> minecraft) {

View File

@@ -0,0 +1,55 @@
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java
@@ -19,6 +_,10 @@
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 +_,10 @@
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 +_,30 @@
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
}

View File

@@ -0,0 +1,288 @@
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -672,4 +_,285 @@
}
// 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<Enchantment, Integer> 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<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> getAttributeModifiers() {
+ return getItemMeta().getAttributeModifiers();
+ }
+
+ @Override
+ public com.google.common.collect.Multimap<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> getAttributeModifiers(org.bukkit.inventory.EquipmentSlot slot) {
+ return getItemMeta().getAttributeModifiers(slot);
+ }
+
+ @Override
+ public java.util.Collection<org.bukkit.attribute.AttributeModifier> 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<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> 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
}

View File

@@ -0,0 +1,10 @@
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
@@ -36,6 +_,7 @@
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

View File

@@ -0,0 +1,29 @@
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
@@ -73,4 +_,26 @@
this.setMaximumRepairCost(legacy.getMaximumRepairCost());
}
}
+
+ // Purpur start - Anvil API
+ @Override
+ public boolean canBypassCost() {
+ return this.container.bypassCost;
+ }
+
+ @Override
+ public void setBypassCost(boolean bypassCost) {
+ this.container.bypassCost = bypassCost;
+ }
+
+ @Override
+ public boolean canDoUnsafeEnchants() {
+ return this.container.canDoUnsafeEnchants;
+ }
+
+ @Override
+ public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) {
+ this.container.canDoUnsafeEnchants = canDoUnsafeEnchants;
+ }
+ // Purpur end - Anvil API
}

View File

@@ -0,0 +1,10 @@
--- a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
+++ b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
@@ -260,6 +_,7 @@
}
static {
+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressInitLegacyMaterialError) // Purpur - Logger settings (suppressing pointless logs)
LOGGER.warn("Initializing Legacy Material Support. Unless you have legacy plugins and/or data this is a bug!"); // Paper - Improve logging and errors; doesn't need to be an error
if (MinecraftServer.getServer() != null && MinecraftServer.getServer().isDebugging()) {
new Exception().printStackTrace();

View File

@@ -0,0 +1,35 @@
--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
+++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
@@ -703,4 +_,32 @@
meta.setCanPlaceOn(materials);
}
// Paper end
+ // Purpur start - Adopt MaterialRerouting
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
+ public static void addFuel(Server server, Material material, int burnTime) {
+ server.addFuel(material, burnTime);
+ }
+
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
+ public static void removeFuel(Server server, Material material) {
+ server.removeFuel(material);
+ }
+
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
+ @RerouteStatic("org/bukkit/Bukkit")
+ public static void addFuel(Material material, int burnTime) {
+ Bukkit.addFuel(material, burnTime);
+ }
+
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570)
+ @RerouteStatic("org/bukkit/Bukkit")
+ public static void removeFuel(Material material) {
+ Bukkit.removeFuel(material);
+ }
+
+ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/commit/607d909efba516893072b782c0393c53d048210e)
+ public static BlockData getBlockData(ItemStack itemStack, Material material) {
+ return itemStack.getBlockData(MaterialRerouting.transformToBlockType(material));
+ }
+ // Purpur end - Adopt MaterialRerouting
}

View File

@@ -0,0 +1,13 @@
--- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
+++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
@@ -46,4 +_,10 @@
}
}
+ // Purpur start - Explorer Map API
+ @Override
+ public boolean isExplorerMap() {
+ return this.worldMap.isExplorerMap;
+ }
+ // Purpur end - Explorer Map API
}

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
@@ -491,7 +_,7 @@
this.parsePending();
} else {
// this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(this.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 - Rebrand
// 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)
}

View File

@@ -0,0 +1,19 @@
--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java
@@ -23,7 +_,15 @@
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 - Gamemode extra permissions
+ 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 - Gamemode extra permissions
DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "experience", "Allows the user to give themselves or others arbitrary values of experience", PermissionDefault.OP, commands); // Paper - wrong permission; redirects are de-redirected and the root literal name is used, so xp -> 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);

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/spigotmc/TicksPerSecondCommand.java
+++ b/src/main/java/org/spigotmc/TicksPerSecondCommand.java
@@ -47,7 +_,7 @@
}
TextComponent.Builder builder = text();
- builder.append(text("TPS from last 1m, 5m, 15m: ", NamedTextColor.GOLD));
+ builder.append(text("TPS from last 5s, 1m, 5m, 15m: ", NamedTextColor.GOLD)); // Purpur - Add 5 second tps average in /tps
builder.append(Component.join(JoinConfiguration.commas(true), tpsAvg));
sender.sendMessage(builder.asComponent());
if (args.length > 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) {

View File

@@ -0,0 +1,53 @@
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -25,7 +_,7 @@
private volatile boolean stopping;
private WatchdogThread(long timeoutTime, boolean restart) {
- super("Paper Watchdog Thread");
+ super("Watchdog Thread"); // Purpur - use a generic name - Rebrand
this.timeoutTime = timeoutTime;
this.restart = restart;
this.earlyWarningEvery = Math.min(GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime);
@@ -75,14 +_,14 @@
this.lastEarlyWarning = currentTime;
if (isLongTimeout) {
logger.log(Level.SEVERE, "------------------------------");
- logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug."); // Paper
+ logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Purpur bug."); // Paper // Purpur - Rebrand
logger.log(Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author");
logger.log(Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring");
logger.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");
logger.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");
- logger.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");
+ logger.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 - Rebrand
logger.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
- logger.log(Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion());
+ logger.log(Level.SEVERE, "Purpur version: " + Bukkit.getServer().getVersion()); // Purpur - Rebrand
if (net.minecraft.world.level.Level.lastPhysicsProblem != null) {
logger.log(Level.SEVERE, "------------------------------");
@@ -102,12 +_,12 @@
}
// Paper end
} else {
- logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---");
+ logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // Purpur - Rebrand
logger.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
}
// Paper end - Different message for short timeout
logger.log(Level.SEVERE, "------------------------------");
- logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):"); // Paper
+ logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Purpur!):"); // Paper // Purpur - Rebrand
FeatureHooks.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - log detailed tick information
WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.threadId(), Integer.MAX_VALUE), logger);
logger.log(Level.SEVERE, "------------------------------");
@@ -120,7 +_,7 @@
WatchdogThread.dumpThread(thread, logger);
}
} else {
- logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---");
+ logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH ---"); // Purpur - Rebrand
}
logger.log(Level.SEVERE, "------------------------------");

View File

@@ -0,0 +1,20 @@
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -2,7 +_,16 @@
<Configuration status="WARN" shutdownHook="disable">
<Appenders>
<Queue name="ServerGuiConsole">
- <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />
+ <!-- Purpur start - copied from TerminalConsole -->
+ <PatternLayout>
+ <LoggerNamePatternSelector defaultPattern="%highlightGUIError{[%d{HH:mm:ss} %level]: [%logger] %stripAnsi{%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]: %stripAnsi{%msg}%n%xEx{full}}" />
+ </LoggerNamePatternSelector>
+ </PatternLayout>
+ <!-- Purpur end -->
</Queue>
<TerminalConsole name="TerminalConsole">
<PatternLayout>

View File

@@ -0,0 +1,36 @@
--- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
+++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
@@ -46,6 +_,7 @@
Set<String> foundPerms = new HashSet<>();
for (CommandNode<CommandSourceStack> 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 +_,25 @@
}
private static final List<String> 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"
);