我先放张效果图:
这张图中主要实现的功能就是消息同步,后来我又加了执行命令的功能,有兴趣的可以自己改改玩233
上图插件的介绍地址:http://mcbbs.net/thread-682777-1-1.html
项目开源地址:http://git.oschina.net/chenxuuu/Minecraft-QQGroupPlubin
gitosc的readme中我写了双方通讯的socket格式了,如下:
协议食用指南:
插件通过socket通讯,使用2333端口
客户端:minecraft插件实现
服务端:cq插件实现
===========================
通讯协议举例:
客户端与服务端每秒发送/接收一次数据
客户端→服务端(minecraft插件→cq插件)发送字符串举例:xxxxxxxx]][[ccccc]][[dawdasdasdasdasd]][[asadsdasdasd
分割符号为“]][[”(如果这一秒内有多条消息的话会这样)
服务端→客户端(cq插件→minecraft插件)发送字符串举例:|||||xxxxxxxx|||||command>ccccc|||||<player>dawdasdasdasdasd
分割符号为“|||||”(如果这一秒内有多条消息的话会这样),当分割开后,字符串中包含“<”符号的,将直接在服务器全局发送;字符串中包含“command>”符号且不包含“<”符号的,将作为命令发送到控制台,其他数据将舍弃(如xxxxxxxx那一个字符串)。
我就稍微再贴一下两边的核心代码吧。
java端:
java这个语言我是没接触过的,所以代码基本都是请教的Balrogs_xt大佬(博客地址:http://www.acgxt.com/),还有万能的百度
java端实现的功能是socket的客户端、并且作为minecraft插件输出和执行各种数据,代码不长,我就全部贴在这里了:
package com.sweetcreeper.qqplugin; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; public class Qqplugin extends JavaPlugin implements Listener { public String player="none233"; public String msg="none233"; @Override//重写父类的方法 public void onEnable() { getLogger().info("QQGroupMessagePlugin is started successfully!"); //注册监听 Bukkit.getPluginManager().registerEvents(this,this); new BukkitRunnable(){ int s = 0;//设置定10秒后执行某段代码 @Override public void run(){ if(s>=60) { Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), "tm abc 发钱啦!"); Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), "eco give * 1"); s=0; } else { s++; } //s--;//迭代递减,我看官方的教程是没这个的,我没试过,你也可以删除试试 //if(s==0){ //这个写10秒后执行的代码(假如定义的定时器每次是1秒) // cancel();//cancel用来取消定时器 //}else{ //这里可以写每次触发定时器执行的代码 try { //1.创建客户端Socket,指定服务器地址和端口 Socket socket=new Socket("localhost", 2333); //2.获取输出流,向服务器端发送信息 OutputStream os=socket.getOutputStream();//字节输出流 PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流 if(player!="none233") { pw.write(msg); player="none233"; } else { pw.write("getmsg"); } pw.flush(); socket.shutdownOutput();//关闭输出流 //3.获取输入流,并读取服务器端的响应信息 InputStream is=socket.getInputStream(); BufferedReader br=new BufferedReader(new InputStreamReader(is)); String info=null; info=br.readLine(); String[] sourceStrArray=info.split("\\|\\|\\|\\|\\|"); for(int i=0;i<sourceStrArray.length;i++) { if(sourceStrArray[i].indexOf("<")!=-1) { Bukkit.broadcastMessage(sourceStrArray[i]); } else if(sourceStrArray[i].indexOf("command>")!=-1) { Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), sourceStrArray[i].replace("command>", "")); if(player!="none233") { msg+="]][[<提示>"+sourceStrArray[i].replace("command>", "")+"已执行"; } else { player="ok"; msg="<提示>"+sourceStrArray[i].replace("command>", "")+"已执行"; } } } //Bukkit.broadcastMessage("debug:"+info); //4.关闭资源 br.close(); is.close(); pw.close(); os.close(); socket.close(); } catch (UnknownHostException e) { //e.printStackTrace(); } catch (IOException e) { //e.printStackTrace(); } //} } }.runTaskTimer(this, 0L, 20L);//参数是,主类、延迟、多少秒运行一次,比如5秒那就是5*20L } @Override public void onDisable() { getLogger().info("QQGroupMessagePlugin is stoped successfully!"); } @EventHandler public void onPlayerSay(AsyncPlayerChatEvent event) { if(player!="none233") { msg+="]][[<"+event.getPlayer().getName()+">"+event.getMessage(); //player="ok"; } else { player="ok"; msg="<"+event.getPlayer().getName()+">"+event.getMessage(); } //Bukkit.broadcastMessage("player:"+event.getPlayer().getName()+",msg:"+event.getMessage()); } @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { if(player!="none233") { msg+="]][[<消息>"+event.getPlayer().getName()+"上线了"; } else { player="ok"; msg="<消息>"+event.getPlayer().getName()+"上线了"; } } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { if(player!="none233") { msg+="]][[<消息>"+event.getPlayer().getName()+"掉线了"; } else { player="ok"; msg="<消息>"+event.getPlayer().getName()+"掉线了"; } } }
基本的思路就是前面写的那个格式:“xxxxxxxx]][[ccccc]][[dawdasdasdasdasd]][[asadsdasdasd”
C#端:
c#端作为酷q插件,在socket通讯中作为服务端
我把网上找来的socket代码稍作修改:
/// <summary> /// 监听客户端连接 /// </summary> private static void ListenClientConnect() { while (true) { Socket clientSocket = serverSocket.Accept(); //clientSocket.Send(Encoding.ASCII.GetBytes("Server Say Hello\r\n")); Socket myClientSocket = (Socket)clientSocket; int receiveNumber; receiveNumber = myClientSocket.Receive(result); //Console.WriteLine("接收客户端 {0} 消息{1}", myClientSocket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(result, 0, receiveNumber)); string replay = Encoding.UTF8.GetString(result, 0, receiveNumber); if (replay.IndexOf("<") != -1) { if (replay.IndexOf("]][[") != -1) { try { string[] str2; str2 = replay.Split(new string[] { "]][[" }, StringSplitOptions.None); foreach (string i in str2) { CQ.SendGroupMessage(GroupSet, i); } } catch { } } else { CQ.SendGroupMessage(GroupSet, replay); } } clientSocket.Send(Encoding.UTF8.GetBytes("ok233")); clientSocket.Send(Encoding.UTF8.GetBytes(mcmsg)); mcmsg = ""; myClientSocket.Shutdown(SocketShutdown.Both); myClientSocket.Close(); } }
上面那段作为多线程部分后台死循环运行。
然后定义几个全局变量:
private static byte[] result = new byte[4096]; private static int myProt = 2333; // 端口 static Socket serverSocket;
在插件初始化时,打开该线程:
IPAddress ip = IPAddress.Parse("127.0.0.1"); serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverSocket.Bind(new IPEndPoint(ip, myProt)); // 绑定 IP 地址:端口 serverSocket.Listen(10); // 设定最多 10 个排队连接请求 // 通过 Clientsoket 发送数据 Thread myThread = new Thread(ListenClientConnect); myThread.Start();
最后只是消息处理问题了,只要让结果消息+="|||||"和别的东西就可以了。
注意的地方:
如果普通消息中你没有强制插入"<"符号,可能后导致命令输入漏洞,所以我从c#到java输出的普通聊天记录的格式是[群消息]<玩家id>消息内容
其他:没东西啦
转载保留版权:晨旭的博客 » 《基于socket的酷Q机器人与Minecraft消息同步功能的实现》如果喜欢可以: 点击右侧上方的邮件订阅,订阅本站
因为没有自动换行右边的侧边栏把代码挡住了2333
老文章,反正也是垃圾代码,没人看的吧