mirror of
https://github.com/PaperMC/Paper.git
synced 2026-11-04 17:00:09 -05:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
065a01eb16 | ||
|
|
3c257d3639 | ||
|
|
eb180d5aeb | ||
|
|
5f210bb2d6 | ||
|
|
a033e3b9ef |
No files matched your search
@@ -122,7 +122,6 @@ public net.minecraft.tags.TagEntry id
|
||||
public net.minecraft.tags.TagEntry required
|
||||
public net.minecraft.tags.TagEntry tag
|
||||
public net.minecraft.util.datafix.fixes.BlockStateData register(ILcom/mojang/serialization/Dynamic;[Lcom/mojang/serialization/Dynamic;)V
|
||||
public net.minecraft.util.datafix.fixes.EntityCustomNameToComponentFix fixCustomName(Lcom/mojang/serialization/DynamicOps;Ljava/lang/String;Ljava/lang/String;)Lcom/mojang/serialization/Dynamic;
|
||||
public net.minecraft.util.datafix.fixes.ItemIdFix ITEM_NAMES
|
||||
public net.minecraft.util.datafix.fixes.ItemSpawnEggFix ID_TO_ENTITY
|
||||
public net.minecraft.world.BossEvent color
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package io.papermc.paper;
|
||||
|
||||
import io.papermc.paper.chat.ChatRenderer;
|
||||
import io.papermc.paper.chat.ChatTypeParameter;
|
||||
import io.papermc.paper.chat.vanilla.ChatTypeRenderer;
|
||||
import io.papermc.paper.world.damagesource.CombatEntry;
|
||||
import io.papermc.paper.world.damagesource.FallLocationType;
|
||||
import net.kyori.adventure.chat.ChatType;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.util.Services;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.damage.DamageEffect;
|
||||
@@ -15,7 +10,6 @@ import org.bukkit.entity.LivingEntity;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Static bridge to the server internals.
|
||||
@@ -79,9 +73,5 @@ public interface InternalAPIBridge {
|
||||
* @return combat entry
|
||||
*/
|
||||
CombatEntry createCombatEntry(DamageSource damageSource, float damage, @Nullable FallLocationType fallLocationType, float fallDistance);
|
||||
|
||||
ChatType createAdventureChatType(String format, String narration, Style style, List<ChatTypeParameter> parameters);
|
||||
|
||||
ChatRenderer createVanillaChatRenderer(ChatTypeRenderer renderer);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package io.papermc.paper.chat;
|
||||
|
||||
import io.papermc.paper.InternalAPIBridge;
|
||||
import io.papermc.paper.chat.vanilla.ChatTypeRenderer;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -69,27 +67,4 @@ public interface ChatRenderer {
|
||||
@ApiStatus.OverrideOnly
|
||||
Component render(Player source, Component sourceDisplayName, Component message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ChatRenderer} that renders using the vanilla chat renderer that is
|
||||
* viewer aware.
|
||||
*
|
||||
* @param renderer the custom provided {@link ChatTypeRenderer.ViewerAware} implementation
|
||||
* @return a {@link ChatRenderer} that delegates to the given viewer‐aware renderer
|
||||
*/
|
||||
static ChatRenderer chatTypeRenderer(final ChatTypeRenderer.ViewerAware renderer) {
|
||||
return InternalAPIBridge.get().createVanillaChatRenderer(renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ChatRenderer} that renders using the vanilla chat renderer that is
|
||||
* viewer unaware.
|
||||
*
|
||||
* @param renderer the custom provided {@link ChatTypeRenderer.ViewerUnaware} implementation
|
||||
* @return a {@link ChatRenderer} that delegates to the given viewer‐unaware renderer
|
||||
*/
|
||||
static ChatRenderer chatTypeRendererViewerUnaware(final ChatTypeRenderer.ViewerUnaware renderer) {
|
||||
return InternalAPIBridge.get().createVanillaChatRenderer(renderer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package io.papermc.paper.chat;
|
||||
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The available parameters for vanilla chat formatting.
|
||||
* This used to determine the order of how chat content is rendered.
|
||||
*
|
||||
* @see io.papermc.paper.chat.vanilla.ChatTypeRenderer#vanillaChatType(String, String, Style, List)
|
||||
*/
|
||||
public enum ChatTypeParameter {
|
||||
SENDER,
|
||||
TARGET,
|
||||
CONTENT
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import org.jspecify.annotations.Nullable;
|
||||
@NullMarked
|
||||
sealed class ViewerUnawareImpl implements ChatRenderer, ChatRenderer.ViewerUnaware permits ViewerUnawareImpl.Default {
|
||||
private final ViewerUnaware unaware;
|
||||
private @Nullable Component message;
|
||||
|
||||
ViewerUnawareImpl(final ViewerUnaware unaware) {
|
||||
this.unaware = unaware;
|
||||
@@ -23,7 +24,10 @@ sealed class ViewerUnawareImpl implements ChatRenderer, ChatRenderer.ViewerUnawa
|
||||
|
||||
@Override
|
||||
public Component render(final Player source, final Component sourceDisplayName, final Component message) {
|
||||
return this.unaware.render(source, sourceDisplayName, message);
|
||||
if (this.message == null) {
|
||||
this.message = this.unaware.render(source, sourceDisplayName, message);
|
||||
}
|
||||
return this.message;
|
||||
}
|
||||
|
||||
static final class Default extends ViewerUnawareImpl implements ChatRenderer.Default {
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package io.papermc.paper.chat.vanilla;
|
||||
|
||||
import net.kyori.adventure.chat.ChatType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The result of rendering a chat message into a specific {@link ChatType}.
|
||||
*/
|
||||
@NullMarked
|
||||
@ApiStatus.NonExtendable
|
||||
public sealed interface ChatTypeRenderResult permits TypeRenderResultImpl {
|
||||
|
||||
/**
|
||||
* Create a render result with no unsigned content component.
|
||||
*
|
||||
* @param bound the {@link ChatType.Bound} containing the formatted message
|
||||
* component and narration text
|
||||
* @return a new {@link ChatTypeRenderResult}
|
||||
*/
|
||||
static ChatTypeRenderResult of(ChatType.Bound bound) {
|
||||
return new TypeRenderResultImpl(bound, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a render result including an unsigned content component.
|
||||
*
|
||||
* @param bound the {@link ChatType.Bound}
|
||||
* @param unsignedContent an optional {@link Component} representing unsigned content, or {@code null}
|
||||
* @return a new {@link ChatTypeRenderResult}
|
||||
*/
|
||||
static ChatTypeRenderResult of(ChatType.Bound bound, @Nullable Component unsignedContent) {
|
||||
return new TypeRenderResultImpl(bound, unsignedContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the optional unsigned content component.
|
||||
*
|
||||
* @return the unsigned {@link Component}, or {@code null} if none was provided
|
||||
*/
|
||||
@Nullable
|
||||
Component unsignedContent();
|
||||
|
||||
/**
|
||||
* Retrieves the bound chat type result.
|
||||
*
|
||||
* @return the {@link ChatType.Bound} instance for this render
|
||||
*/
|
||||
ChatType.Bound boundChat();
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package io.papermc.paper.chat.vanilla;
|
||||
|
||||
import io.papermc.paper.InternalAPIBridge;
|
||||
import io.papermc.paper.chat.ChatTypeParameter;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.chat.ChatType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a renderer implementation that utilizes Minecraft's {@link ChatType} system for
|
||||
* rendering on the client.
|
||||
* <p>
|
||||
* Using this API allows additional flexibility such as configurable narration.
|
||||
* <p>
|
||||
* Implementations are either viewer-unaware or viewer-aware, depending on
|
||||
* whether they need to know the audience when rendering.
|
||||
* </p>
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public sealed interface ChatTypeRenderer permits ChatTypeRenderer.ViewerUnaware, ChatTypeRenderer.ViewerAware {
|
||||
|
||||
/**
|
||||
* A chat type renderer that does not consider the viewer when rendering.
|
||||
*/
|
||||
non-sealed interface ViewerUnaware extends ChatTypeRenderer {
|
||||
|
||||
/**
|
||||
* Renders a chat message without regard to a recipient.
|
||||
*
|
||||
* @param source the player who sent the message
|
||||
* @param sourceDisplayName the display name component of the sender
|
||||
* @param message the message content component
|
||||
* @return the rendering result
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
ChatTypeRenderResult render(Player source, Component sourceDisplayName, Component message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A chat type renderer that may customize output based on the viewer.
|
||||
*/
|
||||
non-sealed interface ViewerAware extends ChatTypeRenderer {
|
||||
|
||||
/**
|
||||
* Render a chat message while taking into account who is receiving the message.
|
||||
*
|
||||
* @param source the player who sent the message
|
||||
* @param sourceDisplayName the display name component of the sender
|
||||
* @param message the message content component
|
||||
* @param viewer the audience receiving the message
|
||||
* @return the rendering result
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
ChatTypeRenderResult render(Player source, Component sourceDisplayName, Component message, Audience viewer);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a vanilla {@link ChatType} with custom message formatting and narration.
|
||||
* <p>
|
||||
* The position of each {@link ChatTypeParameter} directly determines which
|
||||
* placeholder it substitutes in both the chat message and the narration output.
|
||||
*
|
||||
* @param format the pattern for rendering chat messages (e.g. "<%s> %s")
|
||||
* @param narration the pattern for the narration of chat messages (e.g. "%s says %s")
|
||||
* @param style the {@link Style} to apply to the rendered message
|
||||
* @param parameters an ordered list of {@link ChatTypeParameter}s
|
||||
*
|
||||
* @return a new {@link ChatType} instance with the specified format, narration,style, and parameters
|
||||
*/
|
||||
static ChatType vanillaChatType(String format, String narration, Style style, List<ChatTypeParameter> parameters) {
|
||||
return InternalAPIBridge.get().createAdventureChatType(format, narration, style, parameters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package io.papermc.paper.chat.vanilla;
|
||||
|
||||
import net.kyori.adventure.chat.ChatType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@ApiStatus.Internal
|
||||
record TypeRenderResultImpl(ChatType.Bound boundChat, @Nullable Component unsignedContent) implements ChatTypeRenderResult {
|
||||
}
|
||||
@@ -28206,10 +28206,10 @@ index b30f56fbc1fd17259a1d05dc9155fffcab292ca1..11fed81a4696ba18440e755c3b8a5ca3
|
||||
this.generatingStep = generatingStep;
|
||||
this.cache = cache;
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index b80e6d91b8dafc9782bb7796f9855b360fd86f65..990945a9f23830f11bb1e07485fb81b0470dc2e8 100644
|
||||
index eda176c96bcf3d67650722ffce33863edfbdea9e..a7a07ebe6ceed99fa768b6834e350fe2f51a6950 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -1338,7 +1338,7 @@ public abstract class PlayerList {
|
||||
@@ -1332,7 +1332,7 @@ public abstract class PlayerList {
|
||||
|
||||
public void setViewDistance(int viewDistance) {
|
||||
this.viewDistance = viewDistance;
|
||||
@@ -28218,7 +28218,7 @@ index b80e6d91b8dafc9782bb7796f9855b360fd86f65..990945a9f23830f11bb1e07485fb81b0
|
||||
|
||||
for (ServerLevel serverLevel : this.server.getAllLevels()) {
|
||||
if (serverLevel != null) {
|
||||
@@ -1349,7 +1349,7 @@ public abstract class PlayerList {
|
||||
@@ -1343,7 +1343,7 @@ public abstract class PlayerList {
|
||||
|
||||
public void setSimulationDistance(int simulationDistance) {
|
||||
this.simulationDistance = simulationDistance;
|
||||
|
||||
+48
-72
@@ -1395,7 +1395,7 @@
|
||||
throw new IllegalArgumentException("Expected packet sequence nr >= 0");
|
||||
} else {
|
||||
this.ackBlockChangesUpTo = Math.max(sequence, this.ackBlockChangesUpTo);
|
||||
@@ -1356,20 +_,38 @@
|
||||
@@ -1356,23 +_,37 @@
|
||||
@Override
|
||||
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket packet) {
|
||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||
@@ -1425,16 +1425,15 @@
|
||||
|
||||
@Override
|
||||
public void handleChat(ServerboundChatPacket packet) {
|
||||
+ // CraftBukkit start - async chat
|
||||
+ // SPIGOT-3638
|
||||
+ if (this.server.isStopped()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ if (this.server.isStopped()) return; // CraftBukkit - SPIGOT-3638
|
||||
Optional<LastSeenMessages> optional = this.unpackAndApplyLastSeen(packet.lastSeenMessages());
|
||||
if (!optional.isEmpty()) {
|
||||
this.tryHandleChat(packet.message(), () -> {
|
||||
@@ -1381,25 +_,45 @@
|
||||
+ if (this.player.hasDisconnected()) return; // CraftBukkit
|
||||
PlayerChatMessage signedMessage;
|
||||
try {
|
||||
signedMessage = this.getSignedMessage(packet, optional.get());
|
||||
@@ -1381,10 +_,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1442,31 +1441,30 @@
|
||||
- Component component = this.server.getChatDecorator().decorate(this.player, signedMessage.decoratedContent());
|
||||
- this.chatMessageChain.append(completableFuture, filteredText -> {
|
||||
- PlayerChatMessage playerChatMessage = signedMessage.withUnsignedContent(component).filter(filteredText.mask());
|
||||
+ CompletableFuture<FilteredText> completableFuture = this.filterTextPacket(signedMessage.signedContent()).thenApplyAsync(java.util.function.Function.identity(), this.server.chatExecutor); // CraftBukkit - async chat
|
||||
+ CompletableFuture<Component> componentFuture = this.server.getChatDecorator().decorate(this.player, null, signedMessage.decoratedContent()); // Paper - Adventure
|
||||
+ // Paper start - Adventure
|
||||
+ // Make decoration async too, just message decoding has to stay in order, unfortunately blocked by commands
|
||||
+ CompletableFuture<FilteredText> completableFuture = this.filterTextPacket(signedMessage.signedContent()).thenApplyAsync(java.util.function.Function.identity(), this.server.chatExecutor);
|
||||
+ CompletableFuture<Component> componentFuture = this.server.getChatDecorator().decorate(this.player, null, signedMessage.decoratedContent());
|
||||
+
|
||||
+ this.chatMessageChain.append(CompletableFuture.allOf(completableFuture, componentFuture), ($) -> { // Paper - Adventure
|
||||
+ PlayerChatMessage playerChatMessage = signedMessage.withUnsignedContent(componentFuture.join()).filter(completableFuture.join().mask()); // Paper - Adventure
|
||||
+ this.chatMessageChain.append(CompletableFuture.allOf(completableFuture, componentFuture), ($) -> {
|
||||
+ PlayerChatMessage playerChatMessage = signedMessage.withUnsignedContent(componentFuture.join()).filter(completableFuture.join().mask());
|
||||
+ // Paper end - Adventure
|
||||
this.broadcastChatMessage(playerChatMessage);
|
||||
});
|
||||
- });
|
||||
+ }, false); // CraftBukkit - async chat
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1394,12 +_,31 @@
|
||||
@Override
|
||||
public void handleChatCommand(ServerboundChatCommandPacket packet) {
|
||||
this.tryHandleChat(packet.command(), () -> {
|
||||
+ // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands
|
||||
+ if (this.player.hasDisconnected()) {
|
||||
+ if (this.player.hasDisconnected() || this.player.isRemoved()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.performUnsignedChatCommand(packet.command());
|
||||
- this.detectRateSpam();
|
||||
- });
|
||||
+ this.detectRateSpam("/" + packet.command()); // Spigot
|
||||
+ }, true); // CraftBukkit - sync commands
|
||||
});
|
||||
}
|
||||
|
||||
private void performUnsignedChatCommand(String command) {
|
||||
@@ -1478,101 +1476,79 @@
|
||||
+
|
||||
+ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(this.getCraftPlayer(), prefixedCommand, new org.bukkit.craftbukkit.util.LazyPlayerSet(this.server));
|
||||
+ this.cserver.getPluginManager().callEvent(event);
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ command = event.getMessage().substring(1);
|
||||
+ // CraftBukkit end
|
||||
ParseResults<CommandSourceStack> parseResults = this.parseCommand(command);
|
||||
if (this.server.enforceSecureProfile() && SignableCommand.hasSignableArguments(parseResults)) {
|
||||
LOGGER.error(
|
||||
@@ -1416,28 +_,57 @@
|
||||
@@ -1416,13 +_,21 @@
|
||||
Optional<LastSeenMessages> optional = this.unpackAndApplyLastSeen(packet.lastSeenMessages());
|
||||
if (!optional.isEmpty()) {
|
||||
this.tryHandleChat(packet.command(), () -> {
|
||||
+ // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands
|
||||
+ if (this.player.hasDisconnected()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ if (this.player.hasDisconnected()) return; // CraftBukkit - SPIGOT-7346: Prevent disconnected players from executing commands
|
||||
this.performSignedChatCommand(packet, optional.get());
|
||||
- this.detectRateSpam();
|
||||
- });
|
||||
+ this.detectRateSpam("/" + packet.command()); // Spigot
|
||||
+ }, true); // CraftBukkit - sync commands
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void performSignedChatCommand(ServerboundChatCommandSignedPacket packet, LastSeenMessages lastSeenMessages) {
|
||||
+ // CraftBukkit start
|
||||
+ String command = "/" + packet.command();
|
||||
+ if (org.spigotmc.SpigotConfig.logCommands) { // Paper - Add missing SpigotConfig logCommands check
|
||||
+ LOGGER.info("{} issued server command: {}", this.player.getScoreboardName(), command);
|
||||
+ } // Paper - Add missing SpigotConfig logCommands check
|
||||
+
|
||||
+ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(this.getCraftPlayer(), command, new org.bukkit.craftbukkit.util.LazyPlayerSet(this.server));
|
||||
+ this.cserver.getPluginManager().callEvent(event);
|
||||
+ command = event.getMessage().substring(1);
|
||||
+ // Paper start
|
||||
+ final String slashCommand = "/" + packet.command();
|
||||
+ if (org.spigotmc.SpigotConfig.logCommands) {
|
||||
+ LOGGER.info("{} issued server command: {}", this.player.getScoreboardName(), slashCommand);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
ParseResults<CommandSourceStack> parseResults = this.parseCommand(packet.command());
|
||||
|
||||
Map<String, PlayerChatMessage> map;
|
||||
try {
|
||||
+ // Paper - Always parse the original command to add to the chat chain
|
||||
map = this.collectSignedArguments(packet, SignableCommand.of(parseResults), lastSeenMessages);
|
||||
} catch (SignedMessageChain.DecodeException var6) {
|
||||
this.handleMessageDecodeFailure(var6);
|
||||
@@ -1433,11 +_,30 @@
|
||||
return;
|
||||
}
|
||||
|
||||
+ // Paper start - Fix cancellation and message changing
|
||||
+ if (event.isCancelled()) {
|
||||
+ // Only now are we actually good to return
|
||||
+ // Paper start
|
||||
+ // Parsing commands to be on main :cryingandshaking: - if that weren't the case, both this and chat could be pushed off to the chat message chain immediately
|
||||
+ // Always decode message arguments before cancelling, else the chat chain will be broken
|
||||
+ if (this.player.isRemoved()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // Remove signed parts if the command was changed
|
||||
+ if (!command.equals(packet.command())) {
|
||||
+ parseResults = this.parseCommand(command);
|
||||
+ final org.bukkit.event.player.PlayerCommandPreprocessEvent event = new org.bukkit.event.player.PlayerCommandPreprocessEvent(this.getCraftPlayer(), slashCommand, new org.bukkit.craftbukkit.util.LazyPlayerSet(this.server));
|
||||
+ if (!event.callEvent()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ final String updatedCommand = event.getMessage().substring(1);
|
||||
+ if (!updatedCommand.equals(packet.command())) {
|
||||
+ // Remove signed parts if the command was changed
|
||||
+ parseResults = this.parseCommand(updatedCommand);
|
||||
+ map = Collections.emptyMap();
|
||||
+ }
|
||||
+ // Paper end - Fix cancellation and message changing
|
||||
+
|
||||
+ // Paper end
|
||||
CommandSigningContext commandSigningContext = new CommandSigningContext.SignedArguments(map);
|
||||
parseResults = Commands.mapSource(
|
||||
parseResults, commandSourceStack -> commandSourceStack.withSigningContext(commandSigningContext, this.chatMessageChain)
|
||||
);
|
||||
- this.server.getCommands().performCommand(parseResults, packet.command());
|
||||
+ this.server.getCommands().performCommand(parseResults, command); // CraftBukkit
|
||||
+ this.server.getCommands().performCommand(parseResults, updatedCommand); // Paper
|
||||
}
|
||||
|
||||
private void handleMessageDecodeFailure(SignedMessageChain.DecodeException exception) {
|
||||
@@ -1501,14 +_,20 @@
|
||||
return dispatcher.parse(command, this.player.createCommandSourceStack());
|
||||
}
|
||||
@@ -1503,7 +_,7 @@
|
||||
|
||||
- private void tryHandleChat(String message, Runnable handler) {
|
||||
+ private void tryHandleChat(String message, Runnable handler, boolean sync) { // CraftBukkit
|
||||
private void tryHandleChat(String message, Runnable handler) {
|
||||
if (isChatMessageIllegal(message)) {
|
||||
- this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"));
|
||||
- } else if (this.player.getChatVisibility() == ChatVisiblity.HIDDEN) {
|
||||
+ this.disconnectAsync(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add proper async disconnect
|
||||
+ } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales
|
||||
} else if (this.player.getChatVisibility() == ChatVisiblity.HIDDEN) {
|
||||
this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false));
|
||||
} else {
|
||||
this.player.resetLastActionTime();
|
||||
- this.server.execute(handler);
|
||||
+ // CraftBukkit start
|
||||
+ if (sync) {
|
||||
+ this.server.execute(handler);
|
||||
+ } else {
|
||||
+ handler.run();
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1520,7 +_,7 @@
|
||||
var10000 = Optional.of(lastSeenMessages);
|
||||
} catch (LastSeenMessagesValidator.ValidationException var5) {
|
||||
@@ -2607,7 +2583,7 @@
|
||||
this.chatMessageChain
|
||||
.append(
|
||||
() -> {
|
||||
+ server.executeBlocking(() -> { // Paper - Broadcast chat session update sync
|
||||
+ server.executeBlocking(() -> { // Paper - Broadcast chat session update sync, the used broadcast method isn't thread safe
|
||||
this.player.setChatSession(chatSession);
|
||||
this.server
|
||||
.getPlayerList()
|
||||
|
||||
@@ -1013,51 +1013,45 @@
|
||||
|
||||
public void broadcastSystemMessage(Component message, boolean bypassHiddenChat) {
|
||||
this.broadcastSystemMessage(message, serverPlayer -> message, bypassHiddenChat);
|
||||
@@ -756,20 +_,45 @@
|
||||
@@ -756,20 +_,39 @@
|
||||
}
|
||||
|
||||
public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound boundChatType) {
|
||||
- this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, boundChatType);
|
||||
+ // Paper start
|
||||
+ this.broadcastChatMessage(message, sender, io.papermc.paper.adventure.ChatProcessor.ChatContentProvider.ofStaticViewerUnaware(boundChatType, null));
|
||||
+ this.broadcastChatMessage(message, sender, boundChatType, null);
|
||||
+ }
|
||||
+ public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, io.papermc.paper.adventure.ChatProcessor.ChatContentProvider chatContentProvider) {
|
||||
+ public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound boundChatType, @Nullable Function<net.kyori.adventure.audience.Audience, Component> unsignedFunction) {
|
||||
+ // Paper end
|
||||
+ this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, chatContentProvider); // Paper
|
||||
+ this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, boundChatType, unsignedFunction); // Paper
|
||||
}
|
||||
|
||||
private void broadcastChatMessage(
|
||||
PlayerChatMessage message, Predicate<ServerPlayer> shouldFilterMessageTo, @Nullable ServerPlayer sender, ChatType.Bound boundChatType
|
||||
) {
|
||||
+ // Paper start
|
||||
+ this.broadcastChatMessage(message, shouldFilterMessageTo, sender, io.papermc.paper.adventure.ChatProcessor.ChatContentProvider.ofStaticViewerUnaware(boundChatType, null));
|
||||
+ this.broadcastChatMessage(message, shouldFilterMessageTo, sender, boundChatType, null);
|
||||
+ }
|
||||
+ private void broadcastChatMessage(PlayerChatMessage message, Predicate<ServerPlayer> shouldFilterMessageTo, @Nullable ServerPlayer sender, io.papermc.paper.adventure.ChatProcessor.ChatContentProvider chatContentProvider) {
|
||||
+ io.papermc.paper.adventure.ChatProcessor.RenderedResult consoleChat = chatContentProvider.render(this.server.console);
|
||||
+ boolean isViewerUnaware = chatContentProvider.isViewerUnaware();
|
||||
+ public void broadcastChatMessage(PlayerChatMessage message, Predicate<ServerPlayer> shouldFilterMessageTo, @Nullable ServerPlayer sender, ChatType.Bound boundChatType, @Nullable Function<net.kyori.adventure.audience.Audience, Component> unsignedFunction) {
|
||||
+ // Paper end
|
||||
boolean flag = this.verifyChatTrusted(message);
|
||||
- this.server.logChatMessage(message.decoratedContent(), boundChatType, flag ? null : "Not Secure");
|
||||
+ this.server.logChatMessage(consoleChat.unsignedOrElse(message.decoratedContent()), consoleChat.bound(), flag ? null : "Not Secure"); // Paper
|
||||
+ this.server.logChatMessage((unsignedFunction == null ? message.decoratedContent() : unsignedFunction.apply(this.server.console)), boundChatType, flag ? null : "Not Secure"); // Paper
|
||||
OutgoingChatMessage outgoingChatMessage = OutgoingChatMessage.create(message);
|
||||
boolean flag1 = false;
|
||||
|
||||
+ Packet<?> disguised = sender != null && isViewerUnaware ? new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(outgoingChatMessage.content(), consoleChat.bound()) : null; // Paper - don't send player chat packets from vanished players
|
||||
+ Packet<?> disguised = sender != null && unsignedFunction == null ? new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(outgoingChatMessage.content(), boundChatType) : null; // Paper - don't send player chat packets from vanished players
|
||||
for (ServerPlayer serverPlayer : this.players) {
|
||||
boolean flag2 = shouldFilterMessageTo.test(serverPlayer);
|
||||
- serverPlayer.sendChatMessage(outgoingChatMessage, flag2, boundChatType);
|
||||
+ // Paper start - don't send player chat packets from vanished players
|
||||
+ if (sender != null && !serverPlayer.getBukkitEntity().canSee(sender.getBukkitEntity())) {
|
||||
+ if (isViewerUnaware) {
|
||||
+ serverPlayer.connection.send(disguised);
|
||||
+ } else {
|
||||
+ io.papermc.paper.adventure.ChatProcessor.RenderedResult renderedFrame = chatContentProvider.render(serverPlayer.getBukkitEntity());
|
||||
+ serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(renderedFrame.unsignedOrElse(outgoingChatMessage.content()), renderedFrame.bound()));
|
||||
+ }
|
||||
+ serverPlayer.connection.send(unsignedFunction != null
|
||||
+ ? new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(unsignedFunction.apply(serverPlayer.getBukkitEntity()), boundChatType)
|
||||
+ : disguised);
|
||||
+ continue;
|
||||
+ }
|
||||
+ io.papermc.paper.adventure.ChatProcessor.RenderedResult playerChat = isViewerUnaware ? consoleChat : chatContentProvider.render(serverPlayer.getBukkitEntity());
|
||||
+ serverPlayer.sendChatMessage(outgoingChatMessage, flag2, playerChat.bound(), playerChat.unsigned());
|
||||
+ serverPlayer.sendChatMessage(outgoingChatMessage, flag2, boundChatType, unsignedFunction == null ? null : unsignedFunction.apply(serverPlayer.getBukkitEntity()));
|
||||
+ // Paper end
|
||||
flag1 |= flag2 && message.isFullyFiltered();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
--- a/net/minecraft/util/datafix/DataFixers.java
|
||||
+++ b/net/minecraft/util/datafix/DataFixers.java
|
||||
@@ -541,6 +_,18 @@
|
||||
@@ -541,6 +_,24 @@
|
||||
Schema schema44 = builder.addSchema(1456, SAME_NAMESPACED);
|
||||
builder.addFixer(new EntityItemFrameDirectionFix(schema44, false));
|
||||
Schema schema45 = builder.addSchema(1458, V1458::new);
|
||||
+ // CraftBukkit start
|
||||
+ builder.addFixer(new com.mojang.datafixers.DataFix(schema45, false) {
|
||||
+ // API allows setting player custom names, so we need to convert them.
|
||||
+ // This does *not* handle upgrades in any other version, but generally those shouldn't need conversion.
|
||||
+ builder.addFixer(new DataFix(schema45, false) {
|
||||
+ @Override
|
||||
+ protected com.mojang.datafixers.TypeRewriteRule makeRule() {
|
||||
+ return this.fixTypeEverywhereTyped("Player CustomName", this.getInputSchema().getType(References.PLAYER), (typed) -> {
|
||||
+ return typed.update(DSL.remainderFinder(), (dynamic) -> {
|
||||
+ return EntityCustomNameToComponentFix.fixCustomName(dynamic.getOps(), dynamic.get("CustomName").asString(""), "minecraft:player");
|
||||
+ protected TypeRewriteRule makeRule() {
|
||||
+ return this.fixTypeEverywhereTyped("Player CustomName", this.getInputSchema().getType(References.PLAYER), typed -> {
|
||||
+ return typed.update(DSL.remainderFinder(), dynamic -> {
|
||||
+ final String customName = dynamic.get("CustomName").asString("");
|
||||
+ if (customName.isEmpty()) {
|
||||
+ return dynamic.remove("CustomName");
|
||||
+ }
|
||||
+ return dynamic.set("CustomName", LegacyComponentDataFixUtils.createPlainTextComponent(dynamic.getOps(), customName));
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
package io.papermc.paper;
|
||||
|
||||
import io.papermc.paper.adventure.ChatProcessor;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.chat.ChatRenderer;
|
||||
import io.papermc.paper.chat.ChatTypeParameter;
|
||||
import io.papermc.paper.chat.vanilla.ChatTypeRenderer;
|
||||
import io.papermc.paper.util.MCUtil;
|
||||
import io.papermc.paper.world.damagesource.CombatEntry;
|
||||
import io.papermc.paper.world.damagesource.FallLocationType;
|
||||
import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper;
|
||||
import io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper;
|
||||
import net.kyori.adventure.chat.ChatType;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.minecraft.Optionull;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.network.chat.ChatTypeDecoration;
|
||||
import net.minecraft.world.damagesource.FallLocation;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.craftbukkit.block.CraftBiome;
|
||||
@@ -25,10 +14,8 @@ import org.bukkit.craftbukkit.entity.CraftLivingEntity;
|
||||
import org.bukkit.damage.DamageEffect;
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
@NullMarked
|
||||
public class PaperServerInternalAPIBridge implements InternalAPIBridge {
|
||||
@@ -84,29 +71,4 @@ public class PaperServerInternalAPIBridge implements InternalAPIBridge {
|
||||
damageSource, damage, fallLocation, fallDistance
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatType createAdventureChatType(final String format, final String narration, final Style style, final List<ChatTypeParameter> parameters) {
|
||||
List<ChatTypeDecoration.Parameter> parameterList = MCUtil.transformUnmodifiable(parameters, chatTypeParameter -> ChatTypeDecoration.Parameter.values()[chatTypeParameter.ordinal()]);
|
||||
net.minecraft.network.chat.Style nmsType = PaperAdventure.asVanilla(style);
|
||||
|
||||
net.minecraft.network.chat.ChatType chatType = new net.minecraft.network.chat.ChatType(
|
||||
new ChatTypeDecoration(format, parameterList, nmsType),
|
||||
new ChatTypeDecoration(narration, parameterList, nmsType)
|
||||
);
|
||||
return new PaperInternalChatType(Holder.direct(chatType));
|
||||
}
|
||||
|
||||
public record PaperInternalChatType(Holder<net.minecraft.network.chat.ChatType> chatType) implements ChatType {
|
||||
|
||||
@Override
|
||||
public @NotNull Key key() {
|
||||
throw new IllegalStateException("Cannot get key for this registry item, because it is not registered.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatRenderer createVanillaChatRenderer(final ChatTypeRenderer renderer) {
|
||||
return ChatProcessor.PaperVanillaChatRenderer.of(renderer);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package io.papermc.paper.adventure;
|
||||
|
||||
import io.papermc.paper.PaperServerInternalAPIBridge;
|
||||
import io.papermc.paper.chat.ChatRenderer;
|
||||
import io.papermc.paper.chat.vanilla.ChatTypeRenderResult;
|
||||
import io.papermc.paper.chat.vanilla.ChatTypeRenderer;
|
||||
import io.papermc.paper.event.player.AbstractChatEvent;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import io.papermc.paper.event.player.ChatEvent;
|
||||
@@ -15,7 +12,6 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
@@ -25,8 +21,8 @@ import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Optionull;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.OutgoingChatMessage;
|
||||
@@ -67,8 +63,7 @@ public final class ChatProcessor {
|
||||
static final int MESSAGE_CHANGED = 1;
|
||||
static final int FORMAT_CHANGED = 2;
|
||||
static final int SENDER_CHANGED = 3; // Not used
|
||||
static final int USES_CHAT_TYPE_RENDERER = 4;
|
||||
private final BitSet flags = new BitSet(4);
|
||||
private final BitSet flags = new BitSet(3);
|
||||
|
||||
public ChatProcessor(final MinecraftServer server, final ServerPlayer player, final PlayerChatMessage message, final boolean async) {
|
||||
this.server = server;
|
||||
@@ -176,11 +171,7 @@ public final class ChatProcessor {
|
||||
private void readModernModifications(final AbstractChatEvent chatEvent, final ChatRenderer originalRenderer) {
|
||||
this.flags.set(MESSAGE_CHANGED, !chatEvent.message().equals(this.paper$originalMessage));
|
||||
if (originalRenderer != chatEvent.renderer()) { // don't set to false if it hasn't changed
|
||||
if (chatEvent.renderer() instanceof PaperVanillaChatRenderer) {
|
||||
this.flags.set(USES_CHAT_TYPE_RENDERER, true);
|
||||
} else {
|
||||
this.flags.set(FORMAT_CHANGED, true);
|
||||
}
|
||||
this.flags.set(FORMAT_CHANGED, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,12 +190,7 @@ public final class ChatProcessor {
|
||||
final ChatType.Bound chatType = ChatType.bind(chatTypeKey, this.player.level().registryAccess(), PaperAdventure.asVanilla(displayName(player)));
|
||||
|
||||
OutgoingChat outgoingChat = viewers instanceof LazyChatAudienceSet lazyAudienceSet && lazyAudienceSet.isLazy() ? new ServerOutgoingChat() : new ViewersOutgoingChat();
|
||||
if (this.flags.get(USES_CHAT_TYPE_RENDERER)) {
|
||||
if (!(renderer instanceof PaperVanillaChatRenderer vanillaChatRenderer)) {
|
||||
throw new IllegalStateException("BUG: This should only be a vanilla renderer");
|
||||
}
|
||||
outgoingChat.sendChatTypeRendered(player, displayName, message, viewers, vanillaChatRenderer, vanillaChatRenderer.viewerUnaware());
|
||||
} else if (this.flags.get(FORMAT_CHANGED)) {
|
||||
if (this.flags.get(FORMAT_CHANGED)) {
|
||||
if (renderer instanceof ChatRenderer.ViewerUnaware unaware) {
|
||||
outgoingChat.sendFormatChangedViewerUnaware(player, PaperAdventure.asVanilla(unaware.render(player, displayName, message)), viewers, chatType);
|
||||
} else {
|
||||
@@ -226,8 +212,6 @@ public final class ChatProcessor {
|
||||
this.sendMessageChanged(player, renderedMessage, viewers, chatType);
|
||||
}
|
||||
|
||||
void sendChatTypeRendered(CraftPlayer player, Component displayName, Component message, Set<Audience> viewers, PaperVanillaChatRenderer vanillaChatRenderer, boolean viewerUnaware);
|
||||
|
||||
void sendFormatChangedViewerAware(CraftPlayer player, Component displayName, Component message, ChatRenderer renderer, Set<Audience> viewers, ChatType.Bound chatType);
|
||||
|
||||
void sendMessageChanged(CraftPlayer player, net.minecraft.network.chat.Component renderedMessage, Set<Audience> viewers, ChatType.Bound chatType);
|
||||
@@ -236,15 +220,9 @@ public final class ChatProcessor {
|
||||
}
|
||||
|
||||
final class ServerOutgoingChat implements OutgoingChat {
|
||||
@Override
|
||||
public void sendChatTypeRendered(final CraftPlayer player, final Component displayName, final Component message, final Set<Audience> viewers, final PaperVanillaChatRenderer vanillaChatRenderer, final boolean viewerUnaware) {
|
||||
ChatContentProvider provider = viewerUnaware ? ChatContentProvider.ofStaticViewerUnaware(vanillaChatRenderer.compute(player, displayName, message, null)) : ChatContentProvider.ofViewerAware((viewer) -> vanillaChatRenderer.compute(player, displayName, message, viewer));
|
||||
ChatProcessor.this.server.getPlayerList().broadcastChatMessage(ChatProcessor.this.message, ChatProcessor.this.player, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFormatChangedViewerAware(CraftPlayer player, Component displayName, Component message, ChatRenderer renderer, Set<Audience> viewers, ChatType.Bound chatType) {
|
||||
ChatProcessor.this.server.getPlayerList().broadcastChatMessage(ChatProcessor.this.message, ChatProcessor.this.player, ChatContentProvider.ofViewerAware((viewer) -> new RenderedResult(chatType, PaperAdventure.asVanilla(renderer.render(player, displayName, message, viewer)))));
|
||||
ChatProcessor.this.server.getPlayerList().broadcastChatMessage(ChatProcessor.this.message, ChatProcessor.this.player, chatType, viewer -> PaperAdventure.asVanilla(renderer.render(player, displayName, message, viewer)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -259,36 +237,27 @@ public final class ChatProcessor {
|
||||
}
|
||||
|
||||
final class ViewersOutgoingChat implements OutgoingChat {
|
||||
@Override
|
||||
public void sendChatTypeRendered(final CraftPlayer player, final Component displayName, final Component message, final Set<Audience> viewers, final PaperVanillaChatRenderer vanillaChatRenderer, final boolean viewerUnaware) {
|
||||
ChatContentProvider provider = viewerUnaware ? ChatContentProvider.ofStaticViewerUnaware(vanillaChatRenderer.compute(player, displayName, message, null)) : ChatContentProvider.ofViewerAware((viewer) -> vanillaChatRenderer.compute(player, displayName, message, viewer));
|
||||
this.broadcastToViewers(viewers, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFormatChangedViewerAware(CraftPlayer player, Component displayName, Component message, ChatRenderer renderer, Set<Audience> viewers, ChatType.Bound chatType) {
|
||||
this.broadcastToViewers(viewers, ChatContentProvider.ofViewerAware((v) -> PaperAdventure.asVanilla(renderer.render(player, displayName, message, v)), chatType));
|
||||
this.broadcastToViewers(viewers, chatType, v -> PaperAdventure.asVanilla(renderer.render(player, displayName, message, v)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageChanged(CraftPlayer player, net.minecraft.network.chat.Component renderedMessage, Set<Audience> viewers, ChatType.Bound chatType) {
|
||||
this.broadcastToViewers(viewers, ChatContentProvider.ofStaticViewerUnaware(chatType, renderedMessage));
|
||||
this.broadcastToViewers(viewers, chatType, $ -> renderedMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOriginal(CraftPlayer player, Set<Audience> viewers, ChatType.Bound chatType) {
|
||||
this.broadcastToViewers(viewers, ChatContentProvider.ofStaticViewerUnaware(chatType, null));
|
||||
this.broadcastToViewers(viewers, chatType, null);
|
||||
}
|
||||
|
||||
private void broadcastToViewers(Collection<Audience> viewers, final ChatContentProvider provider) {
|
||||
private void broadcastToViewers(Collection<Audience> viewers, final ChatType.Bound chatType, final @Nullable Function<Audience, net.minecraft.network.chat.Component> msgFunction) {
|
||||
for (Audience viewer : viewers) {
|
||||
if (acceptsNative(viewer)) {
|
||||
this.sendNative(viewer, provider);
|
||||
this.sendNative(viewer, chatType, msgFunction);
|
||||
} else {
|
||||
final RenderedResult frame = provider.render(viewer);
|
||||
final ChatType.Bound chatType = frame.bound();
|
||||
final net.minecraft.network.chat.@Nullable Component unsigned = frame.unsigned();
|
||||
|
||||
final net.minecraft.network.chat.@Nullable Component unsigned = Optionull.map(msgFunction, f -> f.apply(viewer));
|
||||
final PlayerChatMessage msg = unsigned == null ? ChatProcessor.this.message : ChatProcessor.this.message.withUnsignedContent(unsigned);
|
||||
viewer.sendMessage(msg.adventureView(), this.adventure(chatType));
|
||||
}
|
||||
@@ -334,25 +303,21 @@ public final class ChatProcessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void sendNative(final Audience viewer, final ChatContentProvider provider) {
|
||||
switch (viewer) {
|
||||
case final ConsoleCommandSender sender -> this.sendToServer(provider);
|
||||
case final CraftPlayer craftPlayer -> {
|
||||
final RenderedResult frame = provider.render(viewer);
|
||||
final ChatType.Bound toChatType = frame.bound();
|
||||
final net.minecraft.network.chat.@Nullable Component messageContent = frame.unsigned();
|
||||
|
||||
craftPlayer.getHandle().sendChatMessage(ChatProcessor.this.outgoing, ChatProcessor.this.player.shouldFilterMessageTo(craftPlayer.getHandle()), toChatType, messageContent);
|
||||
}
|
||||
case final ForwardingAudience.Single single -> this.sendNative(single.audience(), provider);
|
||||
default -> throw new IllegalStateException("Should only be a Player or Console or ForwardingAudience.Single pointing to one!");
|
||||
private void sendNative(final Audience viewer, final ChatType.Bound chatType, final @Nullable Function<Audience, net.minecraft.network.chat.Component> msgFunction) {
|
||||
if (viewer instanceof ConsoleCommandSender) {
|
||||
this.sendToServer(chatType, msgFunction);
|
||||
} else if (viewer instanceof CraftPlayer craftPlayer) {
|
||||
craftPlayer.getHandle().sendChatMessage(ChatProcessor.this.outgoing, ChatProcessor.this.player.shouldFilterMessageTo(craftPlayer.getHandle()), chatType, Optionull.map(msgFunction, f -> f.apply(viewer)));
|
||||
} else if (viewer instanceof ForwardingAudience.Single single) {
|
||||
this.sendNative(single.audience(), chatType, msgFunction);
|
||||
} else {
|
||||
throw new IllegalStateException("Should only be a Player or Console or ForwardingAudience.Single pointing to one!");
|
||||
}
|
||||
}
|
||||
|
||||
private void sendToServer(final ChatContentProvider chatContentProvider) {
|
||||
final RenderedResult frame = chatContentProvider.render(ChatProcessor.this.server.console);
|
||||
final PlayerChatMessage toConsoleMessage = frame.unsigned() == null ? ChatProcessor.this.message : ChatProcessor.this.message.withUnsignedContent(frame.unsigned());
|
||||
ChatProcessor.this.server.logChatMessage(toConsoleMessage.decoratedContent(), frame.bound(), ChatProcessor.this.server.getPlayerList().verifyChatTrusted(toConsoleMessage) ? null : "Not Secure");
|
||||
private void sendToServer(final ChatType.Bound chatType, final @Nullable Function<Audience, net.minecraft.network.chat.Component> msgFunction) {
|
||||
final PlayerChatMessage toConsoleMessage = msgFunction == null ? ChatProcessor.this.message : ChatProcessor.this.message.withUnsignedContent(msgFunction.apply(ChatProcessor.this.server.console));
|
||||
ChatProcessor.this.server.logChatMessage(toConsoleMessage.decoratedContent(), chatType, ChatProcessor.this.server.getPlayerList().verifyChatTrusted(toConsoleMessage) ? null : "Not Secure");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,113 +381,4 @@ public final class ChatProcessor {
|
||||
static boolean canYouHearMe(final HandlerList handlers) {
|
||||
return handlers.getRegisteredListeners().length > 0;
|
||||
}
|
||||
|
||||
public record PaperVanillaChatRenderer(ChatTypeRenderer.ViewerAware vanillaChatTypeRenderer, boolean viewerUnaware) implements ChatRenderer {
|
||||
|
||||
/*
|
||||
We wrap the viewer unaware implementation in a viewer aware implementation in order to prevent code complexity.
|
||||
In this case, the viewerUnaware flag on this renderer indicates that optimizations can occur.
|
||||
We just ignore the viewer param as its treated as effectively undefined when viewerUnaware is true.
|
||||
*/
|
||||
public static ChatRenderer of(ChatTypeRenderer renderer) {
|
||||
return switch (renderer) {
|
||||
case ChatTypeRenderer.ViewerAware viewerAware -> new PaperVanillaChatRenderer(viewerAware, false);
|
||||
case ChatTypeRenderer.ViewerUnaware viewerUnaware -> new PaperVanillaChatRenderer((source, sourceDisplayName, message, viewer) -> viewerUnaware.render(source, sourceDisplayName, message), true);
|
||||
};
|
||||
}
|
||||
|
||||
// api compatability renderer
|
||||
@Override
|
||||
public Component render(final Player source, final Component sourceDisplayName, final Component message, final Audience viewer) {
|
||||
ChatType.Bound nms = compute(source, sourceDisplayName, message, viewer).bound();
|
||||
ChatType chatTypeHolder = nms.chatType().value();
|
||||
|
||||
return PaperAdventure.asAdventure(chatTypeHolder.chat().decorate(PaperAdventure.asVanilla(message), nms));
|
||||
}
|
||||
|
||||
private RenderedResult compute(final Player source, final Component sourceDisplayName, final Component message, final Audience viewer) {
|
||||
ChatTypeRenderResult result = vanillaChatTypeRenderer.render(source, sourceDisplayName, message, viewer);
|
||||
return new RenderedResult(toHandle(((CraftPlayer) source).getHandle(), result.boundChat()), PaperAdventure.asVanilla(result.unsignedContent()));
|
||||
}
|
||||
|
||||
public static net.minecraft.network.chat.ChatType.Bound toHandle(ServerPlayer player, net.kyori.adventure.chat.ChatType.Bound boundChatType) {
|
||||
Holder<ChatType> chatTypeHolder = toNmsChatType(player, boundChatType.type());
|
||||
|
||||
return new net.minecraft.network.chat.ChatType.Bound(
|
||||
chatTypeHolder,
|
||||
io.papermc.paper.adventure.PaperAdventure.asVanilla(boundChatType.name()),
|
||||
Optional.ofNullable(io.papermc.paper.adventure.PaperAdventure.asVanilla(boundChatType.target()))
|
||||
);
|
||||
}
|
||||
|
||||
private static Holder<ChatType> toNmsChatType(ServerPlayer player, net.kyori.adventure.chat.ChatType chatType) {
|
||||
net.minecraft.core.Registry<net.minecraft.network.chat.ChatType> chatTypeRegistry = player.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.CHAT_TYPE);
|
||||
if (chatType instanceof PaperServerInternalAPIBridge.PaperInternalChatType paperInternalChatType) {
|
||||
return paperInternalChatType.chatType();
|
||||
}
|
||||
|
||||
return chatTypeRegistry.get(ResourceKey.create(Registries.CHAT_TYPE, PaperAdventure.asVanilla(chatType.key())))
|
||||
.orElseThrow(() -> new IllegalStateException("Expected a registered chat type!"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
This class is used for providing chat type / unsigned content to users.
|
||||
|
||||
The isViewerUnaware flag indicates that render may be called with an undefined audience, which can be
|
||||
used for slight optimizations.
|
||||
*/
|
||||
public interface ChatContentProvider {
|
||||
|
||||
static ChatContentProvider ofViewerAware(Function<Audience, net.minecraft.network.chat.Component> function, ChatType.Bound chatType) {
|
||||
return ofViewerAware((v) -> new RenderedResult(chatType, function.apply(v)));
|
||||
}
|
||||
|
||||
static ChatContentProvider ofViewerAware(Function<Audience, RenderedResult> function) {
|
||||
return new ChatContentProvider() {
|
||||
@Override
|
||||
public RenderedResult render(final Audience audience) {
|
||||
return function.apply(audience);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewerUnaware() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ChatContentProvider ofStaticViewerUnaware(ChatType.Bound chatType, net.minecraft.network.chat.@Nullable Component unsignedContent) {
|
||||
return ofStaticViewerUnaware(new RenderedResult(chatType, unsignedContent));
|
||||
}
|
||||
|
||||
static ChatContentProvider ofStaticViewerUnaware(RenderedResult result) {
|
||||
record ViewerUnawareStatic(RenderedResult staticFrame) implements ChatContentProvider {
|
||||
|
||||
@Override
|
||||
public RenderedResult render(final Audience audience) {
|
||||
return this.staticFrame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewerUnaware() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return new ViewerUnawareStatic(result);
|
||||
}
|
||||
|
||||
RenderedResult render(Audience audience);
|
||||
|
||||
boolean isViewerUnaware();
|
||||
}
|
||||
|
||||
public record RenderedResult(ChatType.Bound bound, net.minecraft.network.chat.@Nullable Component unsigned) {
|
||||
|
||||
public net.minecraft.network.chat.Component unsignedOrElse(net.minecraft.network.chat.Component component) {
|
||||
return this.unsigned != null ? this.unsigned : component;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user