Message API: 1.19.1 changes (#2411)

* BREAKING: Add server param to game message events

* Update javadocs

* Update testmod

* Fix param name in testmod

* Update ServerMessageEvents.java
This commit is contained in:
apple502j 2022-07-25 00:07:05 +09:00 committed by GitHub
parent c558657297
commit e6c0642c7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 28 deletions

View file

@ -41,10 +41,12 @@ import net.fabricmc.fabric.api.event.EventFactory;
* function. If not given, the message decorator will run in the default phase, which is between
* the content phase and the styling phase.
*
* <p>When implementing a message decorator, it is <strong>very important that the decorator be
* pure; i.e. return the same text when called multiple times for the same arguments (message and
* sender)</strong> - otherwise the server detects a mismatch between the preview and the actual message,
* and discards the message because it was improperly signed.
* <p>The message decorator's result is cached (as of 1.19.1) if the chat preview is enabled.
* If the original message did not change between the last preview and submission, the decorator
* is not called during submission and the cached preview is used instead. Note that the
* decorator can still be called during submission if the chat preview is disabled, the
* sent message was not the same as the previewed message, or if text filtering was enabled and
* it produced a different message.
*
* <p>Example of registering a content phase message decorator:
*

View file

@ -18,6 +18,7 @@ package net.fabricmc.fabric.api.message.v1;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.filter.FilteredMessage;
import net.minecraft.server.network.ServerPlayerEntity;
@ -40,9 +41,9 @@ public final class ServerMessageEvents {
* only if {@link #ALLOW_COMMAND_MESSAGE} event did not block the message,
* and after triggering {@link #COMMAND_MESSAGE} event.
*/
public static final Event<AllowChatMessage> ALLOW_CHAT_MESSAGE = EventFactory.createArrayBacked(AllowChatMessage.class, handlers -> (message, sender, typeKey) -> {
public static final Event<AllowChatMessage> ALLOW_CHAT_MESSAGE = EventFactory.createArrayBacked(AllowChatMessage.class, handlers -> (message, sender, params) -> {
for (AllowChatMessage handler : handlers) {
if (!handler.allowChatMessage(message, sender, typeKey)) return false;
if (!handler.allowChatMessage(message, sender, params)) return false;
}
return true;
@ -57,9 +58,9 @@ public final class ServerMessageEvents {
* the remaining listeners will not be called (if any), and {@link #GAME_MESSAGE}
* event will not be triggered.
*/
public static final Event<AllowGameMessage> ALLOW_GAME_MESSAGE = EventFactory.createArrayBacked(AllowGameMessage.class, handlers -> (message, typeKey) -> {
public static final Event<AllowGameMessage> ALLOW_GAME_MESSAGE = EventFactory.createArrayBacked(AllowGameMessage.class, handlers -> (server, message, overlay) -> {
for (AllowGameMessage handler : handlers) {
if (!handler.allowGameMessage(message, typeKey)) return false;
if (!handler.allowGameMessage(server, message, overlay)) return false;
}
return true;
@ -78,9 +79,9 @@ public final class ServerMessageEvents {
* {@link #ALLOW_CHAT_MESSAGE} and {@link #CHAT_MESSAGE} events will also be
* triggered after triggering {@link #COMMAND_MESSAGE}.
*/
public static final Event<AllowCommandMessage> ALLOW_COMMAND_MESSAGE = EventFactory.createArrayBacked(AllowCommandMessage.class, handlers -> (message, source, typeKey) -> {
public static final Event<AllowCommandMessage> ALLOW_COMMAND_MESSAGE = EventFactory.createArrayBacked(AllowCommandMessage.class, handlers -> (message, source, params) -> {
for (AllowCommandMessage handler : handlers) {
if (!handler.allowCommandMessage(message, source, typeKey)) return false;
if (!handler.allowCommandMessage(message, source, params)) return false;
}
return true;
@ -95,9 +96,9 @@ public final class ServerMessageEvents {
* only if {@link #ALLOW_COMMAND_MESSAGE} event did not block the message,
* and after triggering {@link #COMMAND_MESSAGE} event.
*/
public static final Event<ChatMessage> CHAT_MESSAGE = EventFactory.createArrayBacked(ChatMessage.class, handlers -> (message, sender, typeKey) -> {
public static final Event<ChatMessage> CHAT_MESSAGE = EventFactory.createArrayBacked(ChatMessage.class, handlers -> (message, sender, params) -> {
for (ChatMessage handler : handlers) {
handler.onChatMessage(message, sender, typeKey);
handler.onChatMessage(message, sender, params);
}
});
@ -106,9 +107,9 @@ public final class ServerMessageEvents {
* include death messages, join/leave messages, and advancement messages. Is not called
* when {@linkplain #ALLOW_GAME_MESSAGE game messages are blocked}.
*/
public static final Event<GameMessage> GAME_MESSAGE = EventFactory.createArrayBacked(GameMessage.class, handlers -> (message, typeKey) -> {
public static final Event<GameMessage> GAME_MESSAGE = EventFactory.createArrayBacked(GameMessage.class, handlers -> (server, message, overlay) -> {
for (GameMessage handler : handlers) {
handler.onGameMessage(message, typeKey);
handler.onGameMessage(server, message, overlay);
}
});
@ -120,9 +121,9 @@ public final class ServerMessageEvents {
* <p>If the command is executed by a player, {@link #ALLOW_CHAT_MESSAGE} and
* {@link #CHAT_MESSAGE} events will also be triggered after this event.
*/
public static final Event<CommandMessage> COMMAND_MESSAGE = EventFactory.createArrayBacked(CommandMessage.class, handlers -> (message, source, typeKey) -> {
public static final Event<CommandMessage> COMMAND_MESSAGE = EventFactory.createArrayBacked(CommandMessage.class, handlers -> (message, source, params) -> {
for (CommandMessage handler : handlers) {
handler.onCommandMessage(message, source, typeKey);
handler.onCommandMessage(message, source, params);
}
});
@ -157,11 +158,12 @@ public final class ServerMessageEvents {
* prevents the message from being broadcast and the {@link #GAME_MESSAGE} event
* from triggering.
*
* @param server the server that sent the message
* @param message the broadcast message; use {@code message.raw().getContent()} to get the text
* @param overlay true when the message is an overlay
* @param overlay {@code true} when the message is an overlay
* @return {@code true} if the message should be broadcast, otherwise {@code false}
*/
boolean allowGameMessage(Text message, boolean overlay);
boolean allowGameMessage(MinecraftServer server, Text message, boolean overlay);
}
@FunctionalInterface
@ -209,10 +211,11 @@ public final class ServerMessageEvents {
* include death messages, join/leave messages, and advancement messages. Is not called
* when {@linkplain #ALLOW_GAME_MESSAGE game messages are blocked}.
*
* @param server the server that sent the message
* @param message the broadcast message; use {@code message.raw().getContent()} to get the text
* @param overlay true when the message is an overlay
* @param overlay {@code true} when the message is an overlay
*/
void onGameMessage(Text message, boolean overlay);
void onGameMessage(MinecraftServer server, Text message, boolean overlay);
}
@FunctionalInterface

View file

@ -18,13 +18,16 @@ package net.fabricmc.fabric.mixin.message;
import java.util.function.Function;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.filter.FilteredMessage;
@ -35,6 +38,10 @@ import net.fabricmc.fabric.api.message.v1.ServerMessageEvents;
@Mixin(PlayerManager.class)
public class PlayerManagerMixin {
@Shadow
@Final
private MinecraftServer server;
@Inject(method = "broadcast(Lnet/minecraft/server/filter/FilteredMessage;Lnet/minecraft/server/network/ServerPlayerEntity;Lnet/minecraft/network/message/MessageType$Parameters;)V", at = @At("HEAD"), cancellable = true)
private void onSendChatMessage(FilteredMessage<SignedMessage> message, ServerPlayerEntity sender, MessageType.Parameters params, CallbackInfo ci) {
if (!ServerMessageEvents.ALLOW_CHAT_MESSAGE.invoker().allowChatMessage(message, sender, params)) {
@ -47,12 +54,12 @@ public class PlayerManagerMixin {
@Inject(method = "broadcast(Lnet/minecraft/text/Text;Ljava/util/function/Function;Z)V", at = @At("HEAD"), cancellable = true)
private void onSendGameMessage(Text message, Function<ServerPlayerEntity, Text> playerMessageFactory, boolean overlay, CallbackInfo ci) {
if (!ServerMessageEvents.ALLOW_GAME_MESSAGE.invoker().allowGameMessage(message, overlay)) {
if (!ServerMessageEvents.ALLOW_GAME_MESSAGE.invoker().allowGameMessage(this.server, message, overlay)) {
ci.cancel();
return;
}
ServerMessageEvents.GAME_MESSAGE.invoker().onGameMessage(message, overlay);
ServerMessageEvents.GAME_MESSAGE.invoker().onGameMessage(this.server, message, overlay);
}
@Inject(method = "broadcast(Lnet/minecraft/server/filter/FilteredMessage;Lnet/minecraft/server/command/ServerCommandSource;Lnet/minecraft/network/message/MessageType$Parameters;)V", at = @At("HEAD"), cancellable = true)

View file

@ -22,6 +22,7 @@ import java.util.concurrent.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableTextContent;
import net.minecraft.util.Util;
import net.minecraft.util.math.random.Random;
@ -46,6 +47,15 @@ public class ChatTest implements ModInitializer {
return CompletableFuture.completedFuture(message);
});
// Content phase testing, with variable info
ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.CONTENT_PHASE, (sender, message) -> {
if (message.getString().contains("random")) {
return CompletableFuture.completedFuture(Text.of(String.valueOf(Random.create().nextBetween(0, 100))));
}
return CompletableFuture.completedFuture(message);
});
// Basic styling phase testing
ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.STYLING_PHASE, (sender, message) -> {
if (sender != null && sender.getAbilities().creativeMode) {
@ -74,20 +84,20 @@ public class ChatTest implements ModInitializer {
// ServerMessageEvents
ServerMessageEvents.CHAT_MESSAGE.register(
(message, sender, typeKey) -> LOGGER.info("ChatTest: {} sent \"{}\"", sender, message)
(message, sender, params) -> LOGGER.info("ChatTest: {} sent \"{}\"", sender, message)
);
ServerMessageEvents.GAME_MESSAGE.register(
(message, typeKey) -> LOGGER.info("ChatTest: server sent \"{}\"", message)
(server, message, overlay) -> LOGGER.info("ChatTest: server sent \"{}\"", message)
);
ServerMessageEvents.COMMAND_MESSAGE.register(
(message, source, typeKey) -> LOGGER.info("ChatTest: command sent \"{}\"", message)
(message, source, params) -> LOGGER.info("ChatTest: command sent \"{}\"", message)
);
// ServerMessageEvents blocking
ServerMessageEvents.ALLOW_CHAT_MESSAGE.register(
(message, sender, typeKey) -> !message.raw().getContent().getString().contains("sadtater")
(message, sender, params) -> !message.raw().getContent().getString().contains("sadtater")
);
ServerMessageEvents.ALLOW_GAME_MESSAGE.register((message, typeKey) -> {
ServerMessageEvents.ALLOW_GAME_MESSAGE.register((server, message, overlay) -> {
if (message.getContent() instanceof TranslatableTextContent translatable) {
return !translatable.getKey().startsWith("death.attack.badRespawnPoint.");
}
@ -95,7 +105,7 @@ public class ChatTest implements ModInitializer {
return true;
});
ServerMessageEvents.ALLOW_COMMAND_MESSAGE.register(
(message, source, typeKey) -> !message.raw().getContent().getString().contains("sadtater")
(message, source, params) -> !message.raw().getContent().getString().contains("sadtater")
);
}
}