Added Client Message Events ()

* Added Client Message Events

* Applied suggestions and fixed checkstyle

* Inject before fabric-command-api and updated Javadocs

* Updated Javadocs regarding client commands

* Update fabric-message-api-v1/src/client/resources/fabric-message-api-v1.client.mixins.json

Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>

* Updated Javadocs regarding commands

* Fixed duplicated package names

* Updated ClientMessageEvents.java Javadoc

Co-authored-by: Sideroo <109681866+Sideroo@users.noreply.github.com>

* Removed duplicated client commands Javadoc

* Added cancelled sending and receiving events

* Seperated send and receive events and changed event names

* Fixed checkstyle

* Added support for modifying messages

* Added client command test

* Added narration and message indicator support for modifying received messages

* Added tests for modifying messages

* Updated ClientReceiveMessageEvents#CHAT Javadocs

* Small Javadoc fixes

* Added Modify to names

* Always narrate original message

* Removed modifying receive chat message

* Split notify and modify events

* Fixed checkstyle

---------

Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>
Co-authored-by: Sideroo <109681866+Sideroo@users.noreply.github.com>
(cherry picked from commit c85585f870)
This commit is contained in:
Kevin 2023-02-23 05:13:47 -05:00 committed by modmuss50
parent 81e8c5765a
commit 1ee8be400a
9 changed files with 791 additions and 2 deletions
fabric-message-api-v1
build.gradle
src
client
main/resources
testmod
java/net/fabricmc/fabric/test/message
resources

View file

@ -4,3 +4,7 @@ version = getSubprojectVersion(project)
moduleDependencies(project, [
'fabric-api-base'
])
testDependencies(project, [
'fabric-command-api-v2'
])

View file

@ -0,0 +1,259 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.message.v1;
import java.time.Instant;
import com.mojang.authlib.GameProfile;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.text.Text;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
/**
* Contains client-side events triggered when receiving messages.
*/
public final class ClientReceiveMessageEvents {
private ClientReceiveMessageEvents() {
}
/**
* An event triggered when the client receives a chat message,
* which is any message sent by a player. Mods can use this to block the message.
*
* <p>If a listener returned {@code false}, the message will not be displayed,
* the remaining listeners will not be called (if any), and
* {@link #CHAT_CANCELED} will be triggered instead of {@link #CHAT}.
*/
public static final Event<AllowChat> ALLOW_CHAT = EventFactory.createArrayBacked(AllowChat.class, listeners -> (message, signedMessage, sender, params, receptionTimestamp) -> {
for (AllowChat listener : listeners) {
if (!listener.allowReceiveChatMessage(message, signedMessage, sender, params, receptionTimestamp)) {
return false;
}
}
return true;
});
/**
* An event triggered when the client receives a game message,
* which is any message sent by the server.
* Mods can use this to block the message or toggle overlay.
*
* <p>If a listener returned {@code false}, the message will not be displayed,
* the remaining listeners will not be called (if any), and
* {@link #GAME_CANCELED} will be triggered instead of {@link #MODIFY_GAME}.
*
* <p>Overlay is whether the message will be displayed in the action bar.
* To toggle overlay, return false and call
* {@link net.minecraft.client.network.ClientPlayerEntity#sendMessage(Text, boolean) ClientPlayerEntity.sendMessage(message, overlay)}.
*/
public static final Event<AllowGame> ALLOW_GAME = EventFactory.createArrayBacked(AllowGame.class, listeners -> (message, overlay) -> {
for (AllowGame listener : listeners) {
if (!listener.allowReceiveGameMessage(message, overlay)) {
return false;
}
}
return true;
});
/**
* An event triggered when the client receives a game message,
* which is any message sent by the server. Is not called when
* {@linkplain #ALLOW_GAME game messages are blocked}.
* Mods can use this to modify the message.
* Use {@link #GAME} if not modifying the message.
*
* <p>Overlay is whether the message will be displayed in the action bar.
* Use {@link #ALLOW_GAME to toggle overlay}.
*/
public static final Event<ModifyGame> MODIFY_GAME = EventFactory.createArrayBacked(ModifyGame.class, listeners -> (message, overlay) -> {
for (ModifyGame listener : listeners) {
message = listener.modifyReceivedGameMessage(message, overlay);
}
return message;
});
/**
* An event triggered when the client receives a chat message,
* which is any message sent by a player. Is not called when
* {@linkplain #ALLOW_CHAT chat messages are blocked}.
* Mods can use this to listen to the message.
*
* <p>If mods want to modify the message, they should use {@link #ALLOW_CHAT}
* and manually add the new message to the chat hud using {@link ChatHud#addMessage(Text)}
*/
public static final Event<Chat> CHAT = EventFactory.createArrayBacked(Chat.class, listeners -> (message, signedMessage, sender, params, receptionTimestamp) -> {
for (Chat listener : listeners) {
listener.onReceiveChatMessage(message, signedMessage, sender, params, receptionTimestamp);
}
});
/**
* An event triggered when the client receives a game message,
* which is any message sent by the server. Is not called when
* {@linkplain #ALLOW_GAME game messages are blocked}.
* Mods can use this to listen to the message.
*
* <p>Overlay is whether the message will be displayed in the action bar.
* Use {@link #ALLOW_GAME to toggle overlay}.
*/
public static final Event<Game> GAME = EventFactory.createArrayBacked(Game.class, listeners -> (message, overlay) -> {
for (Game listener : listeners) {
listener.onReceiveGameMessage(message, overlay);
}
});
/**
* An event triggered when receiving a chat message is canceled with {@link #ALLOW_CHAT}.
*/
public static final Event<ChatCanceled> CHAT_CANCELED = EventFactory.createArrayBacked(ChatCanceled.class, listeners -> (message, signedMessage, sender, params, receptionTimestamp) -> {
for (ChatCanceled listener : listeners) {
listener.onReceiveChatMessageCanceled(message, signedMessage, sender, params, receptionTimestamp);
}
});
/**
* An event triggered when receiving a game message is canceled with {@link #ALLOW_GAME}.
*
* <p>Overlay is whether the message would have been displayed in the action bar.
*/
public static final Event<GameCanceled> GAME_CANCELED = EventFactory.createArrayBacked(GameCanceled.class, listeners -> (message, overlay) -> {
for (GameCanceled listener : listeners) {
listener.onReceiveGameMessageCanceled(message, overlay);
}
});
@FunctionalInterface
public interface AllowChat {
/**
* Called when the client receives a chat message,
* which is any message sent by a player.
* Returning {@code false} prevents the message from being displayed, and
* {@link #CHAT_CANCELED} will be triggered instead of {@link #CHAT}.
*
* @param message the message received from the server
* @param signedMessage the signed message received from the server (nullable)
* @param sender the sender of the message (nullable)
* @param params the parameters of the message
* @param receptionTimestamp the timestamp when the message was received
* @return {@code true} if the message should be displayed, otherwise {@code false}
*/
boolean allowReceiveChatMessage(Text message, @Nullable SignedMessage signedMessage, @Nullable GameProfile sender, MessageType.Parameters params, Instant receptionTimestamp);
}
@FunctionalInterface
public interface AllowGame {
/**
* Called when the client receives a game message,
* which is any message sent by the server. Returning {@code false}
* prevents the message from being displayed, and
* {@link #GAME_CANCELED} will be triggered instead of {@link #MODIFY_GAME}.
*
* <p>Overlay is whether the message will be displayed in the action bar.
* To toggle overlay, return false and call
* {@link net.minecraft.client.network.ClientPlayerEntity#sendMessage(Text, boolean) ClientPlayerEntity.sendMessage(message, overlay)}.
*
* @param message the message received from the server
* @param overlay whether the message will be displayed in the action bar
* @return {@code true} if the message should be displayed, otherwise {@code false}
*/
boolean allowReceiveGameMessage(Text message, boolean overlay);
}
@FunctionalInterface
public interface ModifyGame {
/**
* Called when the client receives a game message,
* which is any message sent by the server. Is not called when
* {@linkplain #ALLOW_GAME game messages are blocked}.
* Use {@link #GAME} if not modifying the message.
*
* <p>Overlay is whether the message will be displayed in the action bar.
* Use {@link #ALLOW_GAME} to toggle overlay.
*
* @param message the message received from the server
* @param overlay whether the message will be displayed in the action bar
* @return the modified message to display or the original {@code message} if the message is not modified
*/
Text modifyReceivedGameMessage(Text message, boolean overlay);
}
@FunctionalInterface
public interface Chat {
/**
* Called when the client receives a chat message,
* which is any message sent by a player. Is not called when
* {@linkplain #ALLOW_CHAT chat messages are blocked}.
*
* @param message the message received from the server
* @param signedMessage the signed message received from the server (nullable)
* @param sender the sender of the message (nullable)
* @param params the parameters of the message
* @param receptionTimestamp the timestamp when the message was received
*/
void onReceiveChatMessage(Text message, @Nullable SignedMessage signedMessage, @Nullable GameProfile sender, MessageType.Parameters params, Instant receptionTimestamp);
}
@FunctionalInterface
public interface Game {
/**
* Called when the client receives a game message,
* which is any message sent by the server. Is not called when
* {@linkplain #ALLOW_GAME game messages are blocked}.
*
* <p>Overlay is whether the message will be displayed in the action bar.
* Use {@link #ALLOW_GAME} to toggle overlay.
*
* @param message the message received from the server
* @param overlay whether the message will be displayed in the action bar
*/
void onReceiveGameMessage(Text message, boolean overlay);
}
@FunctionalInterface
public interface ChatCanceled {
/**
* Called when receiving a chat message is canceled with {@link #ALLOW_CHAT}.
*
* @param message the message received from the server
* @param signedMessage the signed message received from the server (nullable)
* @param sender the sender of the message (nullable)
* @param params the parameters of the message
* @param receptionTimestamp the timestamp when the message was received
*/
void onReceiveChatMessageCanceled(Text message, @Nullable SignedMessage signedMessage, @Nullable GameProfile sender, MessageType.Parameters params, Instant receptionTimestamp);
}
@FunctionalInterface
public interface GameCanceled {
/**
* Called when receiving a game message is canceled with {@link #ALLOW_GAME}.
*
* @param message the message received from the server
* @param overlay whether the message would have been displayed in the action bar
*/
void onReceiveGameMessageCanceled(Text message, boolean overlay);
}
}

View file

@ -0,0 +1,251 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.client.message.v1;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
/**
* Contains client-side events triggered when sending messages.
*/
public final class ClientSendMessageEvents {
private ClientSendMessageEvents() {
}
/**
* An event triggered when the client is about to send a chat message,
* typically from a client GUI. Mods can use this to block the message.
*
* <p>If a listener returned {@code false}, the message will not be sent,
* the remaining listeners will not be called (if any), and
* {@link #CHAT_CANCELED} will be triggered instead of {@link #MODIFY_CHAT}.
*/
public static final Event<AllowChat> ALLOW_CHAT = EventFactory.createArrayBacked(AllowChat.class, listeners -> (message) -> {
for (AllowChat listener : listeners) {
if (!listener.allowSendChatMessage(message)) {
return false;
}
}
return true;
});
/**
* An event triggered when the client is about to send a command,
* which is whenever the player executes a command
* including client commands registered with {@code fabric-command-api}.
* Mods can use this to block the message.
* The command string does not include a slash at the beginning.
*
* <p>If a listener returned {@code false}, the command will not be sent,
* the remaining listeners will not be called (if any), and
* {@link #COMMAND_CANCELED} will be triggered instead of {@link #MODIFY_COMMAND}.
*/
public static final Event<AllowCommand> ALLOW_COMMAND = EventFactory.createArrayBacked(AllowCommand.class, listeners -> (command) -> {
for (AllowCommand listener : listeners) {
if (!listener.allowSendCommandMessage(command)) {
return false;
}
}
return true;
});
/**
* An event triggered when the client sends a chat message,
* typically from a client GUI. Is not called when {@linkplain
* #ALLOW_CHAT chat messages are blocked}.
* Mods can use this to modify the message.
* Use {@link #CHAT} if not modifying the message.
*/
public static final Event<ModifyChat> MODIFY_CHAT = EventFactory.createArrayBacked(ModifyChat.class, listeners -> (message) -> {
for (ModifyChat listener : listeners) {
message = listener.modifySendChatMessage(message);
}
return message;
});
/**
* An event triggered when the client sends a command,
* which is whenever the player executes a command
* including client commands registered with {@code fabric-command-api}.
* Is not called when {@linkplain #ALLOW_COMMAND command messages are blocked}.
* The command string does not include a slash at the beginning.
* Mods can use this to modify the command.
* Use {@link #COMMAND} if not modifying the command.
*/
public static final Event<ModifyCommand> MODIFY_COMMAND = EventFactory.createArrayBacked(ModifyCommand.class, listeners -> (command) -> {
for (ModifyCommand listener : listeners) {
command = listener.modifySendCommandMessage(command);
}
return command;
});
/**
* An event triggered when the client sends a chat message,
* typically from a client GUI. Is not called when {@linkplain
* #ALLOW_CHAT chat messages are blocked}.
* Mods can use this to listen to the message.
*/
public static final Event<Chat> CHAT = EventFactory.createArrayBacked(Chat.class, listeners -> (message) -> {
for (Chat listener : listeners) {
listener.onSendChatMessage(message);
}
});
/**
* An event triggered when the client sends a command,
* which is whenever the player executes a command
* including client commands registered with {@code fabric-command-api}.
* Is not called when {@linkplain #ALLOW_COMMAND command messages are blocked}.
* The command string does not include a slash at the beginning.
* Mods can use this to listen to the command.
*/
public static final Event<Command> COMMAND = EventFactory.createArrayBacked(Command.class, listeners -> (command) -> {
for (Command listener : listeners) {
listener.onSendCommandMessage(command);
}
});
/**
* An event triggered when sending a chat message is canceled with {@link #ALLOW_CHAT}.
*/
public static final Event<ChatCanceled> CHAT_CANCELED = EventFactory.createArrayBacked(ChatCanceled.class, listeners -> (message) -> {
for (ChatCanceled listener : listeners) {
listener.onSendChatMessageCanceled(message);
}
});
/**
* An event triggered when sending a command is canceled with {@link #ALLOW_COMMAND}.
* The command string does not include a slash at the beginning.
*/
public static final Event<CommandCanceled> COMMAND_CANCELED = EventFactory.createArrayBacked(CommandCanceled.class, listeners -> (command) -> {
for (CommandCanceled listener : listeners) {
listener.onSendCommandMessageCanceled(command);
}
});
@FunctionalInterface
public interface AllowChat {
/**
* Called when the client is about to send a chat message,
* typically from a client GUI. Returning {@code false}
* prevents the message from being sent, and
* {@link #CHAT_CANCELED} will be triggered instead of {@link #MODIFY_CHAT}.
*
* @param message the message that will be sent to the server
* @return {@code true} if the message should be sent, otherwise {@code false}
*/
boolean allowSendChatMessage(String message);
}
@FunctionalInterface
public interface AllowCommand {
/**
* Called when the client is about to send a command,
* which is whenever the player executes a command
* including client commands registered with {@code fabric-command-api}.
* Returning {@code false} prevents the command from being sent, and
* {@link #COMMAND_CANCELED} will be triggered instead of {@link #MODIFY_COMMAND}.
* The command string does not include a slash at the beginning.
*
* @param command the command that will be sent to the server, without a slash at the beginning.
* @return {@code true} if the command should be sent, otherwise {@code false}
*/
boolean allowSendCommandMessage(String command);
}
@FunctionalInterface
public interface ModifyChat {
/**
* Called when the client sends a chat message,
* typically from a client GUI. Is not called when {@linkplain
* #ALLOW_CHAT chat messages are blocked}.
* Use {@link #CHAT} if not modifying the message.
*
* @param message the message that will be sent to the server
* @return the modified message that will be sent to the server
*/
String modifySendChatMessage(String message);
}
@FunctionalInterface
public interface ModifyCommand {
/**
* Called when the client sends a command,
* which is whenever the player executes a command
* including client commands registered with {@code fabric-command-api}.
* Is not called when {@linkplain #ALLOW_COMMAND command messages are blocked}.
* The command string does not include a slash at the beginning.
* Use {@link #COMMAND} if not modifying the command.
*
* @param command the command that will be sent to the server, without a slash at the beginning.
* @return the modified command that will be sent to the server, without a slash at the beginning.
*/
String modifySendCommandMessage(String command);
}
@FunctionalInterface
public interface Chat {
/**
* Called when the client sends a chat message,
* typically from a client GUI. Is not called when {@linkplain
* #ALLOW_CHAT chat messages are blocked}.
*
* @param message the message that will be sent to the server
*/
void onSendChatMessage(String message);
}
@FunctionalInterface
public interface Command {
/**
* Called when the client sends a command,
* which is whenever the player executes a command
* including client commands registered with {@code fabric-command-api}.
* Is not called when {@linkplain #ALLOW_COMMAND command messages are blocked}.
* The command string does not include a slash at the beginning.
*
* @param command the command that will be sent to the server, without a slash at the beginning.
*/
void onSendCommandMessage(String command);
}
@FunctionalInterface
public interface ChatCanceled {
/**
* Called when sending a chat message is canceled with {@link #ALLOW_CHAT}.
*
* @param message the message that is canceled from being sent to the server
*/
void onSendChatMessageCanceled(String message);
}
@FunctionalInterface
public interface CommandCanceled {
/**
* Called when sending a command is canceled with {@link #ALLOW_COMMAND}.
* The command string does not include a slash at the beginning.
*
* @param command the command that is being sent to the server, without a slash at the beginning.
*/
void onSendCommandMessageCanceled(String command);
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.client.message;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.fabricmc.fabric.api.client.message.v1.ClientSendMessageEvents;
/**
* Mixin to {@link ClientPlayNetworkHandler} to listen for sending messages and commands.
* Priority set to 800 to inject before {@code fabric-command-api} so that this api will be called first.
*/
@Mixin(value = ClientPlayNetworkHandler.class, priority = 800)
public abstract class ClientPlayNetworkHandlerMixin {
@Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true)
private void fabric_allowSendChatMessage(String content, CallbackInfo ci) {
if (!ClientSendMessageEvents.ALLOW_CHAT.invoker().allowSendChatMessage(content)) {
ClientSendMessageEvents.CHAT_CANCELED.invoker().onSendChatMessageCanceled(content);
ci.cancel();
}
}
@ModifyVariable(method = "sendChatMessage", at = @At(value = "LOAD", ordinal = 0), ordinal = 0, argsOnly = true)
private String fabric_modifySendChatMessage(String content) {
content = ClientSendMessageEvents.MODIFY_CHAT.invoker().modifySendChatMessage(content);
ClientSendMessageEvents.CHAT.invoker().onSendChatMessage(content);
return content;
}
@Inject(method = "sendChatCommand", at = @At("HEAD"), cancellable = true)
private void fabric_allowSendCommandMessage(String command, CallbackInfo ci) {
if (!ClientSendMessageEvents.ALLOW_COMMAND.invoker().allowSendCommandMessage(command)) {
ClientSendMessageEvents.COMMAND_CANCELED.invoker().onSendCommandMessageCanceled(command);
ci.cancel();
}
}
@ModifyVariable(method = "sendChatCommand", at = @At(value = "LOAD", ordinal = 0), ordinal = 0, argsOnly = true)
private String fabric_modifySendCommandMessage(String command) {
command = ClientSendMessageEvents.MODIFY_COMMAND.invoker().modifySendCommandMessage(command);
ClientSendMessageEvents.COMMAND.invoker().onSendCommandMessage(command);
return command;
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.client.message;
import java.time.Instant;
import com.mojang.authlib.GameProfile;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.network.message.MessageHandler;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.text.Text;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
@Mixin(MessageHandler.class)
public abstract class MessageHandlerMixin {
@Inject(method = "processChatMessageInternal", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;getChatHud()Lnet/minecraft/client/gui/hud/ChatHud;", ordinal = 0), cancellable = true)
private void fabric_onSignedChatMessage(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> cir) {
fabric_onChatMessage(decorated, message, sender, params, receptionTimestamp, cir);
}
@Inject(method = "processChatMessageInternal", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;getChatHud()Lnet/minecraft/client/gui/hud/ChatHud;", ordinal = 1), cancellable = true)
private void fabric_onFilteredSignedChatMessage(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> cir) {
Text filtered = message.filterMask().getFilteredText(message.getSignedContent());
if (filtered != null) {
fabric_onChatMessage(params.applyChatDecoration(filtered), message, sender, params, receptionTimestamp, cir);
}
}
@Inject(method = "method_45745", at = @At("HEAD"), cancellable = true)
private void fabric_onProfilelessChatMessage(MessageType.Parameters params, Text content, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> cir) {
fabric_onChatMessage(params.applyChatDecoration(content), null, null, params, receptionTimestamp, cir);
}
@Unique
private void fabric_onChatMessage(Text message, @Nullable SignedMessage signedMessage, @Nullable GameProfile sender, MessageType.Parameters params, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> cir) {
if (ClientReceiveMessageEvents.ALLOW_CHAT.invoker().allowReceiveChatMessage(message, signedMessage, sender, params, receptionTimestamp)) {
ClientReceiveMessageEvents.CHAT.invoker().onReceiveChatMessage(message, signedMessage, sender, params, receptionTimestamp);
} else {
ClientReceiveMessageEvents.CHAT_CANCELED.invoker().onReceiveChatMessageCanceled(message, signedMessage, sender, params, receptionTimestamp);
cir.setReturnValue(false);
}
}
@Inject(method = "onGameMessage", at = @At("HEAD"), cancellable = true)
private void fabric_allowGameMessage(Text message, boolean overlay, CallbackInfo ci) {
if (!ClientReceiveMessageEvents.ALLOW_GAME.invoker().allowReceiveGameMessage(message, overlay)) {
ClientReceiveMessageEvents.GAME_CANCELED.invoker().onReceiveGameMessageCanceled(message, overlay);
ci.cancel();
}
}
@ModifyVariable(method = "onGameMessage", at = @At(value = "LOAD", ordinal = 0), ordinal = 0, argsOnly = true)
private Text fabric_modifyGameMessage(Text message, Text message1, boolean overlay) {
message = ClientReceiveMessageEvents.MODIFY_GAME.invoker().modifyReceivedGameMessage(message, overlay);
ClientReceiveMessageEvents.GAME.invoker().onReceiveGameMessage(message, overlay);
return message;
}
}

View file

@ -0,0 +1,12 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.client.message",
"compatibilityLevel": "JAVA_17",
"client": [
"ClientPlayNetworkHandlerMixin",
"MessageHandlerMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -21,7 +21,11 @@
},
"description": "Adds message-related hooks.",
"mixins": [
"fabric-message-api-v1.mixins.json"
"fabric-message-api-v1.mixins.json",
{
"config": "fabric-message-api-v1.client.mixins.json",
"environment": "client"
}
],
"custom": {
"fabric-api:module-lifecycle": "experimental"

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.text.Text;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.message.v1.ClientSendMessageEvents;
public class ChatTestClient implements ClientModInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger(ChatTestClient.class);
@Override
public void onInitializeClient() {
//Register test client commands
ClientCommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(ClientCommandManager.literal("block").then(ClientCommandManager.literal("send").executes(context -> {
throw new AssertionError("This client command should be blocked!");
}))));
//Test client send message events
ClientSendMessageEvents.ALLOW_CHAT.register((message) -> {
if (message.contains("block send")) {
LOGGER.info("Blocked chat message: " + message);
return false;
}
return true;
});
ClientSendMessageEvents.MODIFY_CHAT.register((message) -> {
if (message.contains("modify send")) {
LOGGER.info("Modifying chat message: " + message);
return "sending modified chat message";
}
return message;
});
ClientSendMessageEvents.CHAT.register((message -> LOGGER.info("Sent chat message: " + message)));
ClientSendMessageEvents.CHAT_CANCELED.register((message) -> LOGGER.info("Canceled sending chat message: " + message));
//Test client send command events
ClientSendMessageEvents.ALLOW_COMMAND.register((command) -> {
if (command.contains("block send")) {
LOGGER.info("Blocked command message: " + command);
return false;
}
return true;
});
ClientSendMessageEvents.MODIFY_COMMAND.register((command) -> {
if (command.contains("modify send")) {
LOGGER.info("Modifying command message: " + command);
return "sending modified command message";
}
return command;
});
ClientSendMessageEvents.COMMAND.register((command -> LOGGER.info("Sent command message: " + command)));
ClientSendMessageEvents.COMMAND_CANCELED.register((command) -> LOGGER.info("Canceled sending command message: " + command));
//Test client receive message events
ClientReceiveMessageEvents.ALLOW_CHAT.register((message, signedMessage, sender, params, receptionTimestamp) -> {
if (message.getString().contains("block receive")) {
LOGGER.info("Blocked receiving chat message: " + message.getString());
return false;
}
return true;
});
ClientReceiveMessageEvents.CHAT.register((message, signedMessage, sender, params, receptionTimestamp) -> LOGGER.info("Received chat message sent by {} at time {}: {}", sender == null ? "null" : sender.getName(), receptionTimestamp.toEpochMilli(), message.getString()));
ClientReceiveMessageEvents.CHAT_CANCELED.register((message, signedMessage, sender, params, receptionTimestamp) -> LOGGER.info("Cancelled receiving chat message sent by {} at time {}: {}", sender == null ? "null" : sender.getName(), receptionTimestamp.toEpochMilli(), message.getString()));
//Test client receive game message events
ClientReceiveMessageEvents.ALLOW_GAME.register((message, overlay) -> {
if (message.getString().contains("block receive")) {
LOGGER.info("Blocked receiving game message: " + message.getString());
return false;
}
return true;
});
ClientReceiveMessageEvents.MODIFY_GAME.register((message, overlay) -> {
if (message.getString().contains("modify receive")) {
LOGGER.info("Modifying received game message: " + message.getString());
return Text.of("modified receiving game message");
}
return message;
});
ClientReceiveMessageEvents.GAME.register((message, overlay) -> LOGGER.info("Received game message with overlay {}: {}", overlay, message.getString()));
ClientReceiveMessageEvents.GAME_CANCELED.register((message, overlay) -> LOGGER.info("Cancelled receiving game message with overlay {}: {}", overlay, message.getString()));
}
}

View file

@ -6,11 +6,15 @@
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-message-api-v1": "*"
"fabric-message-api-v1": "*",
"fabric-command-api-v2": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.message.ChatTest"
],
"client": [
"net.fabricmc.fabric.test.message.ChatTestClient"
]
}
}