首先,我对netty一窍不通,只是会依葫芦画瓢而已
这篇文章讲了如何用1.12forge在客户端监听收到的数据包https://blog.techno.fish/intercepting-vanilla-minecraft-network-packets/
(唔,如果datapack翻译成数据包,那packet到底该翻译成什么好呢
)
文章里说要覆写SimpleChannelInboundHandler的构造函数,其实写成匿名内部类new SimpleChannelInboundHandler(false){略}也是一样的,至于@Sharable也是没必要写的,只要你保证自己不会重复注册
如果要监听发出的数据包,则要把SimpleChannelInboundHandler换成ChannelDuplexHandler,但其实ChannelDuplexHandler既可以监听收到包,也可以监听发出的包//1.12forge客户端
@SubscribeEvent
public void connect(FMLNetworkEvent.ClientConnectedToServerEvent event) {
event.getManager().channel().pipeline().addBefore("packet_handler", "awa", new ChannelDuplexHandler() {
/*如果要顺便监听收包就写这个函数,不然不用写
public void channelRead(ChannelHandlerContext context, Object packet) throws Exception {
super.channelRead(context, packet);
}
*/
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) throws Exception {
if(packet instanceof CPacketChatMessage) {
ReflectionHelper.setPrivateValue(CPacketChatMessage.class, (CPacketChatMessage)packet, "hello world", 0);
}
super.write(context, packet, channelPromise);
}
});
}复制代码这里修改了客户端发出的聊天数据包,玩家说的任何话都变成了hello world
按理说应该有专门监听收包的存在,盲猜是ChannelOutboundHandlerAdapter,不过我完全不懂
如果要在服务器监听,把ClientConnectedToServerEvent换成ServerConnectionFromClientEvent,高版本这些名字会变,但也大差不差画面转到fabric,相比于forge和bukkit那样的事件总线系统,fabric的事件是真难找,而且fabric里不存在与上文的channel()等效的方法(即返回一个Channel),那就只能进行一个反的射,不知道fabric有没有forge那样辅助反射的方法 private static final MethodHandle channel_mh;
static {
MethodHandle mh=null;
try {
Field f=ClientConnection.class.getDeclaredField("field_11651");
f.setAccessible(true);
mh=MethodHandles.lookup().unreflectGetter(f);
}catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
channel_mh=mh;
}
@Override
public void onInitialize() {
ClientPlayConnectionEvents.INIT.register((handler, client)->{
Channel channel=null;
try {
channel = (Channel) channel_mh.invokeExact(handler.getConnection());
} catch (Throwable e) {
e.printStackTrace();
}
channel.pipeline().addAfter("packet_handler", "awa", new ChannelDuplexHandler() {
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) throws Exception {
if(packet instanceof CustomPayloadC2SPacket) {
CustomPayloadC2SPacket cp=(CustomPayloadC2SPacket)packet;
if(cp.getChannel().equals(CustomPayloadC2SPacket.BRAND))
cp.getData().clear().writeBytes(PacketByteBufs.create().writeString("awa"));
}
super.write(context, packet, channelPromise);
}
});
});
}
复制代码这里修改了客户端发出的BRAND频道的CustomPayloadC2SPacket的内容,而服务端可以这样接收到 ServerPlayNetworking.registerGlobalReceiver(CustomPayloadC2SPacket.BRAND, (server, player, handler, buf, responseSender) -> {
LOGGER.info(buf.readString());
});复制代码[Netty Server IO #1/INFO]: awa复制代码如果要监听服务端收到/发送的包,则把上面的ClientPlayConnectionEvents改成ServerPlayConnectionEvents,如果要监听登录包(或者说握手包)则改成ClientLoginConnectionEvents/ServerLoginConnectionEventsforge似乎监听不了登录包,没有那么早的事件这套方法也可以用在bukkit里,不过bukkit有ProtocolLib就用不着这个了CustomPayload包比较特别,forge、fabric、bukkit自带了接收的方法,上面写的ServerPlayNetworking.registerGlobalReceiver就是其一,这个包在很多地方的教程都有讲https://www.mcbbs.net/thread-873219-1-1.html
(唔,如果datapack翻译成数据包,那packet到底该翻译成什么好呢
文章里说要覆写SimpleChannelInboundHandler的构造函数,其实写成匿名内部类new SimpleChannelInboundHandler(false){略}也是一样的,至于@Sharable也是没必要写的,只要你保证自己不会重复注册
如果要监听发出的数据包,则要把SimpleChannelInboundHandler换成ChannelDuplexHandler,但其实ChannelDuplexHandler既可以监听收到包,也可以监听发出的包//1.12forge客户端
@SubscribeEvent
public void connect(FMLNetworkEvent.ClientConnectedToServerEvent event) {
event.getManager().channel().pipeline().addBefore("packet_handler", "awa", new ChannelDuplexHandler() {
/*如果要顺便监听收包就写这个函数,不然不用写
public void channelRead(ChannelHandlerContext context, Object packet) throws Exception {
super.channelRead(context, packet);
}
*/
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) throws Exception {
if(packet instanceof CPacketChatMessage) {
ReflectionHelper.setPrivateValue(CPacketChatMessage.class, (CPacketChatMessage)packet, "hello world", 0);
}
super.write(context, packet, channelPromise);
}
});
}复制代码这里修改了客户端发出的聊天数据包,玩家说的任何话都变成了hello world
按理说应该有专门监听收包的存在,盲猜是ChannelOutboundHandlerAdapter,不过我完全不懂
如果要在服务器监听,把ClientConnectedToServerEvent换成ServerConnectionFromClientEvent,高版本这些名字会变,但也大差不差画面转到fabric,相比于forge和bukkit那样的事件总线系统,fabric的事件是真难找,而且fabric里不存在与上文的channel()等效的方法(即返回一个Channel),那就只能进行一个反的射,不知道fabric有没有forge那样辅助反射的方法 private static final MethodHandle channel_mh;
static {
MethodHandle mh=null;
try {
Field f=ClientConnection.class.getDeclaredField("field_11651");
f.setAccessible(true);
mh=MethodHandles.lookup().unreflectGetter(f);
}catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
channel_mh=mh;
}
@Override
public void onInitialize() {
ClientPlayConnectionEvents.INIT.register((handler, client)->{
Channel channel=null;
try {
channel = (Channel) channel_mh.invokeExact(handler.getConnection());
} catch (Throwable e) {
e.printStackTrace();
}
channel.pipeline().addAfter("packet_handler", "awa", new ChannelDuplexHandler() {
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) throws Exception {
if(packet instanceof CustomPayloadC2SPacket) {
CustomPayloadC2SPacket cp=(CustomPayloadC2SPacket)packet;
if(cp.getChannel().equals(CustomPayloadC2SPacket.BRAND))
cp.getData().clear().writeBytes(PacketByteBufs.create().writeString("awa"));
}
super.write(context, packet, channelPromise);
}
});
});
}
复制代码这里修改了客户端发出的BRAND频道的CustomPayloadC2SPacket的内容,而服务端可以这样接收到 ServerPlayNetworking.registerGlobalReceiver(CustomPayloadC2SPacket.BRAND, (server, player, handler, buf, responseSender) -> {
LOGGER.info(buf.readString());
});复制代码[Netty Server IO #1/INFO]: awa复制代码如果要监听服务端收到/发送的包,则把上面的ClientPlayConnectionEvents改成ServerPlayConnectionEvents,如果要监听登录包(或者说握手包)则改成ClientLoginConnectionEvents/ServerLoginConnectionEventsforge似乎监听不了登录包,没有那么早的事件这套方法也可以用在bukkit里,不过bukkit有ProtocolLib就用不着这个了CustomPayload包比较特别,forge、fabric、bukkit自带了接收的方法,上面写的ServerPlayNetworking.registerGlobalReceiver就是其一,这个包在很多地方的教程都有讲https://www.mcbbs.net/thread-873219-1-1.html