refactor: proper join and leave messages, not just on entity (un)load events

This commit is contained in:
Erica Marigold 2023-12-26 21:52:42 +05:30
parent d996d21ced
commit 84729afe54
No known key found for this signature in database
GPG key ID: 2768CC0C23D245D1
7 changed files with 104 additions and 40 deletions

View file

@ -1,20 +1,15 @@
package xyz.devcomp;
import java.util.ArrayList;
import xyz.devcomp.config.ConfigHandler;
import xyz.devcomp.config.ConfigModel;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
import net.fabricmc.fabric.api.message.v1.ServerMessageEvents;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.PlayerChatMessage;
import net.minecraft.network.chat.Style;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.sounds.SoundEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,6 +20,8 @@ public class Stinky implements ModInitializer {
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger("stinky");
public static final ConfigModel Config = new ConfigHandler().getConfig();
public static final ResourceLocation EC_SOUND_ID = new ResourceLocation("stinky:ping");
public static SoundEvent EC_SOUND_EVENT = SoundEvent.createVariableRangeEvent(EC_SOUND_ID);
@Override
public void onInitialize() {
@ -33,38 +30,17 @@ public class Stinky implements ModInitializer {
// Proceed with mild caution.
LOGGER.info("Hello from Stinky!");
// TODO: Cleanup logic with a shared function for both join & leave events
// FIXME: Load & Unload events are fired on death and respawn too, not just join and leave
ServerEntityEvents.ENTITY_LOAD.register((Entity entity, ServerLevel world) -> {
ArrayList<String> joinMsgStrings = Stinky.Config.getJoinMessageStrings();
String formattedUsername = entity.getName().toString().replaceAll("literal", "").replaceAll("\\{", "")
.replaceAll("\\}", "");
// Get a random join message and display it as a green system message
entity.sendSystemMessage(Component.literal(String.format(
joinMsgStrings.get((int) (Math.random() * joinMsgStrings.size())), formattedUsername))
.setStyle(Style.EMPTY.withColor(43520)));
});
ServerEntityEvents.ENTITY_UNLOAD.register((Entity entity, ServerLevel world) -> {
ArrayList<String> leaveMsgStrings = Stinky.Config.getLeaveMessageStrings();
String formattedUsername = entity.getName().toString().replaceAll("literal", "").replaceAll("\\{", "")
.replaceAll("\\}", "");
// Get a random leave message and display it as a red system message
entity.sendSystemMessage(Component.literal(String.format(
leaveMsgStrings.get((int) (Math.random() * leaveMsgStrings.size())), formattedUsername))
.setStyle(Style.EMPTY.withColor(11141120)));
});
ServerMessageEvents.CHAT_MESSAGE.register((PlayerChatMessage msg, ServerPlayer plr, ChatType.Bound bound) -> {
String msgString = msg.signedContent();
LOGGER.info("msgString: ", msgString);
if (msgString == ";ec") {
if (msgString == ";ec") {
// We're setting the health to 0, instead of plr.kill(), because this
// abuses a flaw in the graves VanillaTweaks datapack to make the player
// respawn without creating a grave or losing their items
// TODO: Play stinky:ping sound
plr.setHealth(0);
}
});

View file

@ -0,0 +1,31 @@
package xyz.devcomp.mixin;
import java.util.ArrayList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import net.minecraft.network.chat.Component;
import xyz.devcomp.Stinky;
@Mixin(Component.class)
public interface ComponentMixin {
@ModifyVariable(method = "translatable(Ljava/lang/String;[Ljava/lang/Object;)Lnet/minecraft/network/chat/MutableComponent;", at = @At("HEAD"), ordinal = 0)
private static String transformLeaveMessage(String string) {
// We cannot shadow this as a static member on the Component
// class and ComponentMixin is an interface, so we just need to
// fetch the messages on every leave. Not ideal, but whatever
ArrayList<String> leaveMessages = Stinky.Config.getLeaveMessageStrings();
// Since ServerGamePacketListenerImpl broadcasts the message without any
// intermediate variable for the component, the only thing we can do is
// intercept the message in Component#translatable and return a custom message
if (string == "multiplayer.player.left") {
return leaveMessages.get((int) (Math.random() * leaveMessages.size()));
}
return string;
}
}

View file

@ -0,0 +1,47 @@
package xyz.devcomp.mixin;
import java.util.ArrayList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import xyz.devcomp.Stinky;
@Mixin(PlayerList.class)
public class PlayerListMixin {
private ServerPlayer currentPlayer;
private ChatFormatting currentFormattingStyle;
private static final ArrayList<String> JoinMessages = Stinky.Config.getJoinMessageStrings();
@ModifyVariable(method = "placeNewPlayer(Lnet/minecraft/network/Connection;Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/server/network/CommonListenerCookie;)V", at = @At("HEAD"), ordinal = 0)
private ServerPlayer captureServerPlayer(ServerPlayer plyr) {
this.currentPlayer = plyr;
return plyr;
}
@ModifyVariable(method = "placeNewPlayer(Lnet/minecraft/network/Connection;Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/server/network/CommonListenerCookie;)V", at = @At("STORE"), ordinal = 0)
private MutableComponent injectCustomMessage(MutableComponent base) {
this.currentFormattingStyle = ChatFormatting.DARK_GREEN;
return Component.literal(String.format(JoinMessages.get((int) (Math.random() * JoinMessages.size())), this.currentPlayer.getDisplayName().getString()));
}
@ModifyVariable(method = "broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V", at = @At("HEAD"), ordinal = 0)
private Component injectCustomBroadcastFormatting(Component component) {
ChatFormatting currentChatFormattingStyle = this.currentFormattingStyle;
// We assume the next call is a disconnect, because we never really
// know when a disconnect is initiated. In case it's not a disconnect,
// the above injectCustomMessage will reset it back to DARK_GREEN
// for connection
this.currentFormattingStyle = ChatFormatting.DARK_RED;
return component.copy().withStyle(currentChatFormattingStyle);
}
}

View file

@ -13,13 +13,13 @@ import xyz.devcomp.Stinky;
@Mixin(ServerStatusPacketListenerImpl.class)
public class ServerStatusPacketListenerImplMixin {
private static ArrayList<String> MOTDs = Stinky.Config.getMOTDs();
private static final ArrayList<String> MOTDs = Stinky.Config.getMOTDs();
@ModifyVariable(method = "<init>(Lnet/minecraft/network/protocol/status/ServerStatus;Lnet/minecraft/network/Connection;)V", at = @At("HEAD"), ordinal = 0)
private static ServerStatus injected(ServerStatus serverStatus) {
private static ServerStatus injectCustomMOTD(ServerStatus serverStatus) {
return new ServerStatus(
Component.literal(ServerStatusPacketListenerImplMixin.MOTDs
.get((int) (Math.random() * ServerStatusPacketListenerImplMixin.MOTDs.size()))),
Component.literal(MOTDs
.get((int) (Math.random() * MOTDs.size()))),
serverStatus.players(), serverStatus.version(),
serverStatus.favicon(), serverStatus.enforcesSecureChat());
}

View file

@ -0,0 +1,8 @@
{
"my_sound": {
"sounds": [
"stinky:ping"
]
}
}

Binary file not shown.

View file

@ -3,7 +3,9 @@
"package": "xyz.devcomp.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"ServerStatusPacketListenerImplMixin"
"ServerStatusPacketListenerImplMixin",
"ComponentMixin",
"PlayerListMixin"
],
"injectors": {
"defaultRequire": 1