From 979135878c653fb5c504286260447f4b6b86e6a5 Mon Sep 17 00:00:00 2001 From: Jake Potrebic <15055071+Machine-Maker@users.noreply.github.com> Date: Tue, 18 May 2021 01:35:39 -0700 Subject: [PATCH] Add cause and cancel message to PlayerGameModeChangeEvent (#5638) --- ...ditions-to-PlayerGameModeChangeEvent.patch | 111 +++++++++++++++ ...ditions-to-PlayerGameModeChangeEvent.patch | 128 ++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 Spigot-API-Patches/0301-additions-to-PlayerGameModeChangeEvent.patch create mode 100644 Spigot-Server-Patches/0733-additions-to-PlayerGameModeChangeEvent.patch diff --git a/Spigot-API-Patches/0301-additions-to-PlayerGameModeChangeEvent.patch b/Spigot-API-Patches/0301-additions-to-PlayerGameModeChangeEvent.patch new file mode 100644 index 000000000..07c662e58 --- /dev/null +++ b/Spigot-API-Patches/0301-additions-to-PlayerGameModeChangeEvent.patch @@ -0,0 +1,111 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Sat, 15 May 2021 10:04:50 -0700 +Subject: [PATCH] additions to PlayerGameModeChangeEvent + + +diff --git a/src/main/java/org/bukkit/event/player/PlayerGameModeChangeEvent.java b/src/main/java/org/bukkit/event/player/PlayerGameModeChangeEvent.java +index 4b96e0573c7ce32ad8c41124ee9ecab8359318a5..b71f37dc8ac00175efd9275e9d8988bb5f2d9c47 100644 +--- a/src/main/java/org/bukkit/event/player/PlayerGameModeChangeEvent.java ++++ b/src/main/java/org/bukkit/event/player/PlayerGameModeChangeEvent.java +@@ -13,10 +13,22 @@ public class PlayerGameModeChangeEvent extends PlayerEvent implements Cancellabl + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final GameMode newGameMode; ++ // Paper start ++ private final Cause cause; ++ private net.kyori.adventure.text.Component cancelMessage; + ++ @Deprecated // Paper end + public PlayerGameModeChangeEvent(@NotNull final Player player, @NotNull final GameMode newGameMode) { ++ // Paper start ++ this(player, newGameMode, Cause.UNKNOWN, null); ++ } ++ ++ public PlayerGameModeChangeEvent(@NotNull final Player player, @NotNull final GameMode newGameMode, @NotNull Cause cause, @org.jetbrains.annotations.Nullable net.kyori.adventure.text.Component cancelMessage) { ++ // Paper end + super(player); + this.newGameMode = newGameMode; ++ this.cause = cause; // Paper ++ this.cancelMessage = cancelMessage; // Paper + } + + @Override +@@ -49,4 +61,77 @@ public class PlayerGameModeChangeEvent extends PlayerEvent implements Cancellabl + public static HandlerList getHandlerList() { + return handlers; + } ++ // Paper start ++ /** ++ * Gets the cause of this gamemode change. ++ * ++ * @return the cause ++ */ ++ @NotNull ++ public Cause getCause() { ++ return cause; ++ } ++ ++ /** ++ * Only valid if the cause of the gamemode change was directly due to a command.. ++ * Gets the message shown to the command user if the event is cancelled ++ * as a notification that a player's gamemode was not changed. ++ *

++ * This returns {@code null} if the gamemode change was due to a plugin, or a ++ * player joining the game with a gamemode not equal to the server default gamemode ++ * and {@code force-gamemode} is set to true. ++ * ++ * @return the error message shown to the command user, null if not directly caused by a command ++ */ ++ @org.jetbrains.annotations.Nullable ++ public net.kyori.adventure.text.Component cancelMessage() { ++ return cancelMessage; ++ } ++ ++ /** ++ * Sets the message shown to the command user if the event was cancelled. ++ * The message is only shown to cancelled events that are directly called by a command ++ * not by a plugin or a player joining with the wrong gamemode. ++ * ++ * @param message the error message shown to the command user, null to show no message. ++ */ ++ public void cancelMessage(@org.jetbrains.annotations.Nullable net.kyori.adventure.text.Component message) { ++ this.cancelMessage = message; ++ } ++ ++ public enum Cause { ++ ++ /** ++ * A plugin changed the player's gamemode with ++ * {@link Player#setGameMode(GameMode)}. ++ */ ++ PLUGIN, ++ ++ /** ++ * The {@code /gamemode} command was used. ++ */ ++ COMMAND, ++ ++ /** ++ * A player had their gamemode changed as a result of ++ * the {@code /defaultgamemode} command, or they joined ++ * with a gamemode that was not the default gamemode and ++ * {@code force-gamemode} in {@code server.properties} is set to true. ++ */ ++ DEFAULT_GAMEMODE, ++ ++ /** ++ * When the player dies in a hardcore world and has their gamemode ++ * changed to {@link GameMode#SPECTATOR}. ++ */ ++ HARDCORE_DEATH, ++ ++ /** ++ * This cause is only used if a plugin fired their own ++ * {@link PlayerGameModeChangeEvent} and did not include a ++ * cause. Can usually be ignored. ++ */ ++ UNKNOWN, ++ } ++ // Paper end + } diff --git a/Spigot-Server-Patches/0733-additions-to-PlayerGameModeChangeEvent.patch b/Spigot-Server-Patches/0733-additions-to-PlayerGameModeChangeEvent.patch new file mode 100644 index 000000000..2f1c5e243 --- /dev/null +++ b/Spigot-Server-Patches/0733-additions-to-PlayerGameModeChangeEvent.patch @@ -0,0 +1,128 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Sat, 15 May 2021 10:04:43 -0700 +Subject: [PATCH] additions to PlayerGameModeChangeEvent + + +diff --git a/src/main/java/net/minecraft/server/commands/CommandGamemode.java b/src/main/java/net/minecraft/server/commands/CommandGamemode.java +index 376d5eac9c17365266d8a4986bf4c19030454c9a..8da48d9cbadfbe83ae8410cf49d78df49f50fb08 100644 +--- a/src/main/java/net/minecraft/server/commands/CommandGamemode.java ++++ b/src/main/java/net/minecraft/server/commands/CommandGamemode.java +@@ -62,13 +62,13 @@ public class CommandGamemode { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (entityplayer.playerInteractManager.getGameMode() != enumgamemode) { +- entityplayer.a(enumgamemode); +- // CraftBukkit start - handle event cancelling the change +- if (entityplayer.playerInteractManager.getGameMode() != enumgamemode) { +- commandcontext.getSource().sendFailureMessage(new net.minecraft.network.chat.ChatComponentText("Failed to set the gamemode of '" + entityplayer.getName() + "'")); ++ // Paper start - handle event cancelling the change ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = entityplayer.setGamemode(enumgamemode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.COMMAND, net.kyori.adventure.text.Component.text("Failed to set the gamemode of '" + entityplayer.getName() + "'", net.kyori.adventure.text.format.NamedTextColor.RED)); ++ if (event != null && event.isCancelled()) { ++ commandcontext.getSource().sendMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.cancelMessage()), false); + continue; + } +- // CraftBukkit end ++ // Paper end + a((CommandListenerWrapper) commandcontext.getSource(), entityplayer, enumgamemode); + ++i; + } +diff --git a/src/main/java/net/minecraft/server/commands/CommandGamemodeDefault.java b/src/main/java/net/minecraft/server/commands/CommandGamemodeDefault.java +index 5790699b3898910dfde46f588996dd8af42757d1..2a731594c22bb266d3dfef4d64d55b9772565cca 100644 +--- a/src/main/java/net/minecraft/server/commands/CommandGamemodeDefault.java ++++ b/src/main/java/net/minecraft/server/commands/CommandGamemodeDefault.java +@@ -43,7 +43,13 @@ public class CommandGamemodeDefault { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (entityplayer.playerInteractManager.getGameMode() != enumgamemode) { +- entityplayer.a(enumgamemode); ++ // Paper start - handle event cancelling the change ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = entityplayer.setGamemode(enumgamemode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, net.kyori.adventure.text.Component.text("Failed to set the gamemode of '" + entityplayer.getName() + "'", net.kyori.adventure.text.format.NamedTextColor.RED)); ++ if (event != null && event.isCancelled()) { ++ commandlistenerwrapper.sendMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.cancelMessage()), false); ++ continue; ++ } ++ // Paper end + ++i; + } + } +diff --git a/src/main/java/net/minecraft/server/level/EntityPlayer.java b/src/main/java/net/minecraft/server/level/EntityPlayer.java +index 8683d6ddc5d8ce4a302fa6e2665b7dcd64f6a00a..558af73ac16550ee6964c4dce681a404633b2552 100644 +--- a/src/main/java/net/minecraft/server/level/EntityPlayer.java ++++ b/src/main/java/net/minecraft/server/level/EntityPlayer.java +@@ -396,7 +396,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + if (this.locY() > 300) this.setPositionRaw(locX(), 257, locZ()); // Paper - bring down to a saner Y level if out of world + if (nbttagcompound.hasKeyOfType("playerGameType", 99)) { + if (this.getMinecraftServer().getForceGamemode()) { ++ // Paper start - call PlayerGameModeChangeEvent on join for players that do not have the correct gamemode ++ if (this.getMinecraftServer().getGamemode() != EnumGamemode.getById(nbttagcompound.getInt("playerGameType"))) { ++ if (new org.bukkit.event.player.PlayerGameModeChangeEvent(this.getBukkitEntity(), GameMode.getByValue(this.getMinecraftServer().getGamemode().getId()), org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null).callEvent()) { + this.playerInteractManager.a(this.getMinecraftServer().getGamemode(), EnumGamemode.NOT_SET); ++ } else { ++ this.playerInteractManager.a(EnumGamemode.getById(nbttagcompound.getInt("playerGameType")), nbttagcompound.hasKeyOfType("previousPlayerGameType", 3) ? EnumGamemode.getById(nbttagcompound.getInt("previousPlayerGameType")) : EnumGamemode.NOT_SET); // copied from below; if cancelled, set gamemode normally ++ } ++ } else { ++ this.playerInteractManager.a(EnumGamemode.getById(nbttagcompound.getInt("playerGameType")), nbttagcompound.hasKeyOfType("previousPlayerGameType", 3) ? EnumGamemode.getById(nbttagcompound.getInt("previousPlayerGameType")) : EnumGamemode.NOT_SET); // copied from below; if no change needed, set gamemode normally ++ } // Paper end + } else { + this.playerInteractManager.a(EnumGamemode.getById(nbttagcompound.getInt("playerGameType")), nbttagcompound.hasKeyOfType("previousPlayerGameType", 3) ? EnumGamemode.getById(nbttagcompound.getInt("previousPlayerGameType")) : EnumGamemode.NOT_SET); + } +@@ -1792,15 +1801,21 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + + @Override + public void a(EnumGamemode enumgamemode) { ++ // Paper start - Add cause and nullable message to event ++ setGamemode(enumgamemode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null); ++ } ++ ++ public PlayerGameModeChangeEvent setGamemode(EnumGamemode enumgamemode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause, net.kyori.adventure.text.Component message) { ++ // Paper end + // CraftBukkit start + if (enumgamemode == this.playerInteractManager.getGameMode()) { +- return; ++ return null; // Paper + } + +- PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(getBukkitEntity(), GameMode.getByValue(enumgamemode.getId())); ++ PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(getBukkitEntity(), GameMode.getByValue(enumgamemode.getId()), cause, message); // Paper + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { +- return; ++ return event; // Paper + } + // CraftBukkit end + +@@ -1815,6 +1830,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + + this.updateAbilities(); + this.dU(); ++ return event; // Paper + } + + @Override +diff --git a/src/main/java/net/minecraft/server/network/PlayerConnection.java b/src/main/java/net/minecraft/server/network/PlayerConnection.java +index 8c9e97bb093c0e6297397edc71d72deebbcfbed9..fdc824c7b4e9aae29a8ea08a492fbdeb6519ca60 100644 +--- a/src/main/java/net/minecraft/server/network/PlayerConnection.java ++++ b/src/main/java/net/minecraft/server/network/PlayerConnection.java +@@ -2450,7 +2450,7 @@ public class PlayerConnection implements PacketListenerPlayIn { + + this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, false); + if (this.minecraftServer.isHardcore()) { +- this.player.a(EnumGamemode.SPECTATOR); ++ this.player.setGamemode(EnumGamemode.SPECTATOR, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.HARDCORE_DEATH, null); // Paper + ((GameRules.GameRuleBoolean) this.player.getWorldServer().getGameRules().get(GameRules.SPECTATORS_GENERATE_CHUNKS)).a(false, this.minecraftServer); + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 05248f560d643080a3eac581c01aa89fb3709e6c..34395248e3daea47178cb40aad53680fbce73600 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1193,7 +1193,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + throw new IllegalArgumentException("Mode cannot be null"); + } + +- getHandle().a(EnumGamemode.getById(mode.getValue())); ++ getHandle().setGamemode(EnumGamemode.getById(mode.getValue()), org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.PLUGIN, null); // Paper + } + + @Override