基于socket的酷Q机器人与Minecraft消息同步功能的实现

我先放张效果图:

这张图中主要实现的功能就是消息同步,后来我又加了执行命令的功能,有兴趣的可以自己改改玩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>消息内容

其他:没东西啦

2 Comments

发表评论

您的电子邮箱地址不会被公开。