在实际开发过程中,我们常常需要用到网络通信。用到网络通信的地方有很多,例如按下按键释放技能,同步玩家属性,监听图形界面按钮按下,让客户端播放特效等等。那么怎么样进行网络通信呢?
简单网络通信包装类 SimpleNetworkWrapper
为了方便的进行网络通信,Forge提供一个简单的网络通信包装类——SimpleNetworkWrapper。这个类位于net.minecraftforge.fml.common.network.simpleimpl包下。
那么,怎么使用这个类呢?首先我们需要获取一个SimpleNetworkWrapper对象。
- NetworkRegistry.INSTANCE.newSimpleChannel(channel);
在此解释一下channel(频道)参数,该参数是一个字符串(String),表示网络通信频道名,只有在相同的频道,数据才能互相传输。
以上代码可以获取到一个SimpleNetworkWrapper对象,然后我们将这个对象储存至一个静态字段中以便在模组的任何地方调用,比如这样:
- @Mod(modid = ChinaCraft.MODID, name = ChinaCraft.NAME, version = ChinaCraft.VERSION)
- public final class ChinaCraft {
- public static final String MODID = "chinacraft";
- public static final String NAME = "ChinaCraft 2";
- public static final String VERSION = "0.0.1";
- private static SimpleNetworkWrapper network;
- @EventHandler
- public void init(FMLInitializationEvent event) {
- network = NetworkRegistry.INSTANCE.newSimpleChannel(MODID);
- }
- public static SimpleNetworkWrapper getNetwork() {
- return network;
- }
- }
这样,我们就可以使用ChinaCraft.getNetwork()方法在模组的任何地方调用SimpleNetworkWrapper对象了。
消息类和消息处理类
接下来,我们开始编写我们的消息类和消息处理类。消息类需要实现IMessage接口,该类必须要有一个无参构造方法,并且需要实现fromBytes和toBytes方法。
- public class RedPacketMessage implements IMessage {
- public RedPacketMessage() {} //必须要有一个无参构造方法
- @Override
- public void fromBytes(ByteBuf buf) {} //将字节流转换为信息
- @Override
- public void toBytes(ByteBuf buf) {} //将信息转换为字节流
- }
可以看到,fromBytes和toBytes方法都有一个ByteBuf类型的参数,ByteBuf类中提供了很多read和write方法,那么我们该如何使用呢?举个例子:
- public class RedPacketMessage implements IMessage {
- private String sender;
- public RedPacketMessage() {}
- public RedPacketMessage(String sender) {
- this.sender = sender;
- }
- @Override
- public void fromBytes(ByteBuf buf) {
- char chars[] = new char[buf.readInt()]; //读取字符串长度
- for(int i=0;i<chars.length;i++) //读取字符
- chars = buf.readChar();
- sender = String.valueOf(chars); //创建字符串对象
- }
- @Override
- public void toBytes(ByteBuf buf) {
- buf.writeInt(sender.length()); //写入字符串长度
- for(char c:sender.toCharArray()) //写入字符串
- buf.writeChar(c);
- }
提示:可以看到ByteBuf类有些不方便使用,所以可以使用MC实现的PacketBuffer类和Forge提供的ByteBufUtils类。
接下来,我们需要为这个消息编写一个处理类,来处理收到的消息,消息处理类需要实现IMessageHandler接口,并且需要实现onMessage方法。此外,该接口还是一个泛型接口,拥有两个泛型形参REQ extends IMessage和REPLY extends IMessage,其中REQ表示收到的消息类,REPLY表示回复的消息类。
- public class RedPacketMessageHandler implements IMessageHandler<RedPacketMessage, IMessage> {
- @Override
- public IMessage onMessage(RedPacketMessage message, MessageContext ctx) {
- return null; //当你不需要回复消息时,可以将REPLY填写为IMessage,并且返回null;
- }
- }
接下来,我们需要将消息类和消息处理类注册到SimpleNetworkWrapper对象中。
- network.registerMessage(handler,message,id,side);
解释一下registerMessage方法的四个参数,第一个参数handler需要一个IMessageHandler对象,第二个参数message则是对应的消息类的类对象,第三个参数id表示该消息的id,范围为0~255,且不得重复,第四个参数side表示收到消息的对象,Side.SERVER表示消息由客户端发送,服务器接收,Side.CLIENT表示消息由服务端发送,客户端接收。
发送消息
那么,注册好了以后我们怎么发送消息呢?SimpleNetworkWrapper类实现了五个发送方法,分别有不同的作用。
- /**
- * 发送消息给所有玩家(服务端调用该方法)
- */
- public void sendToAll(IMessage message)
- /**
- * 发送消息到指定玩家(服务端调用该方法)
- */
- public void sendTo(IMessage message, EntityPlayerMP player)
- /**
- * 发送消息到指定范围内的所有玩家(服务端调用该方法)
- */
- public void sendToAllAround(IMessage message, NetworkRegistry.TargetPoint point)
- /**
- * 发送消息到指定世界内的所有玩家(服务端调用该方法)
- */
- public void sendToDimension(IMessage message, int dimensionId)
- /**
- * 发送消息到服务器(客户端调用该方法)
- */
- public void sendToServer(IMessage message)
例如:
- ChinaCraft.getNetwork().sendToServer(new RedPacketMessage());
ByteBufUtils优化的字符串处理
- public class RedPacketMessage implements IMessage {
- private String sender;
- public RedPacketMessage() {}
- public RedPacketMessage(String sender) {
- this.sender = sender;
- }
- @Override
- public void fromBytes(ByteBuf buf) {
- sender = ByteBufUtils.readUTF8String(buf);
- }
- @Override
- public void toBytes(ByteBuf buf) {
- ByteBufUtils.writeUTF8String(buf,sender);
- }
本篇教程到这就结束了,感谢您的阅读。
本教程来自ChinaCraft2代码详解——大型模组开发笔记 Github