Minecraft Fabric 1.21.5替换发送至服务器的聊天消息触发无限循环崩溃及以玩家身份发消息的问题求助
Minecraft Fabric 1.21.5替换发送至服务器的聊天消息触发无限循环崩溃及以玩家身份发消息的问题求助
嘿,我看你遇到的这个无限循环崩溃问题其实是Mixin开发里很常见的小坑,咱们来一步步捋清楚问题根源,再给出可行的解决办法~
问题根源
你当前的代码在ClientPlayNetworkHandler.sendChatMessage方法的HEAD位置注入了逻辑,当检测到消息包含foo时,又调用了this.sendChatMessage("bar")——这就形成了无限递归:每次调用sendChatMessage都会触发你的注入代码,而注入代码又再次调用sendChatMessage,游戏的调用栈会一直堆叠,最终导致栈溢出崩溃。
解决方案1:用ModifyArg直接修改发送参数(推荐)
这种方式不需要手动调用sendChatMessage,而是直接修改原方法要发送的消息参数,从根源避免循环。我们可以定位到sendChatMessage方法内部真正发送消息的调用点,直接替换参数:
package org.emil.chatmod.mixin; import net.minecraft.client.network.ClientPlayNetworkHandler; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; @Mixin(ClientPlayNetworkHandler.class) public abstract class ClientPlayNetworkHandlerMixin { @ModifyArg( method = "sendChatMessage", at = @At( value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendChatMessage(Ljava/lang/String;)V" ), index = 0 ) private String modifyChatMessage(String originalMessage) { // 检测原消息,替换内容 if (originalMessage.contains("foo")) { return "bar"; } // 不需要替换时返回原消息 return originalMessage; } }
解决方案2:用Inject取消原调用后发送新消息
如果你更倾向于用Inject的方式,需要在触发替换逻辑时取消原方法的执行,这样就不会和你手动调用的sendChatMessage形成循环:
package org.emil.chatmod.mixin; import net.minecraft.client.network.ClientPlayNetworkHandler; 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; @Mixin(ClientPlayNetworkHandler.class) public abstract class ClientPlayNetworkHandlerMixin { @Shadow public abstract void sendChatMessage(String content); @Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true) private void onSendChatMessage(String message, CallbackInfo ci) { if (message.contains("foo")) { // 取消原方法的执行,避免循环 ci.cancel(); // 发送修改后的消息 this.sendChatMessage("bar"); } } }
这里关键是给Inject添加cancellable = true参数,然后在替换逻辑里调用ci.cancel(),这样原方法的后续执行会被终止。而你发送的bar消息因为不包含foo,不会触发这个if分支,自然也就不会形成循环了。
内容来源于stack exchange




