Unity3D XML(-RPC)和C#

我实际上是在这里回答我自己的问题.

我必须是世界上唯一一个试图这样做的人,但考虑到我用了大约一个星期的时间来解决这个问题 – 我想如果有另一个人想在Unity中使用XML(-RPC) – 我会拯救他们一周的麻烦.

我想做的是与我们的一个游戏服务器谈谈排行榜等事情.这台服务器“会谈”XML-RPC,我很快发现在Unity中这并不容易.

构建XML以发送到我们的服务器

我没有在Unity中找到标准函数来执行此操作而不会增加大量开销.所以我建立了以下程序.

public  string buildXMLRPCRequest(Hashtable FieldArray,string MethodName) 
{
    string  ReturnString = "";

    ReturnString    +=         "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>" +
                        "\n" + "<simpleRPC version=\"0.9\">" +
                        "\n" + "<methodCall>" +
                        "\n" + "<methodName>" + MethodName + "</methodName>" +
                        "\n" + "<vector type=\"struct\">";

    ReturnString    +=  buildNode(FieldArray);

    ReturnString    +=  "\n</vector>" +
                        "\n</methodCall>" +
                        "\n</simpleRPC>";
    return  ReturnString;
}

public  string buildNode(Hashtable FieldArray) 
{
    string  ReturnList = "";

    foreach (DictionaryEntry Item in FieldArray)    {

        string  TypeName    =   "int";
        string  NodeType    =   "scalar";

        Type myType =   Item.Value.GetType();
        string  fieldValue  =   "";

        if (myType == typeof(string) ) {
            TypeName    =   "string";
            fieldValue  =   Item.Value.ToString();
        }

        if (myType == typeof(Hashtable) ) {
            fieldValue  =   buildNode(Item.Value as Hashtable);
            NodeType    =   "vector";
            TypeName    =   "struct";
        }

        if (myType == typeof(int) ) {
            fieldValue  =   Item.Value.ToString();
            TypeName    = "int";
        }

        var ThisNode    =   "\n<" + NodeType + " type=\"" + TypeName + "\" id=\"" + Item.Key + "\">" + fieldValue + "</" + NodeType + ">";
        ReturnList +=   ThisNode;
    }

    return ReturnList;
}

buildXMLRPCRequest用于构建XML.您可以使用要编码的字段来处理HashTable,其中可能包含以下类型的对象:int,string或Hashtable.它将返回一个精美格式化的(简单)XML-RPC字符串,该字符串已准备好发送到我们的服务器.

发送

要将XML发送到我们的服务器,您需要发出一个POST请求,其mime类型设置为text / xml. Unity中不能使用任何标准C#方法,但使用buildXMLRPCRequest逻辑的输出可以很好地工作.它能做什么:

在Unity中发送

我用过这段代码

private     void UnityPostXML(  int Staging,string WebServer,string MethodName,Hashtable   FieldArray)
    {
        string  WebServiceURL   =   "http://LIVESERVER/";
        if (Staging == 1) {
            WebServiceURL       =   "http://TESTSERVER";
        }

        // Encode the text to a UTF8 byte arrray

        string XMLRequest   =   buildXMLRPCRequest(FieldArray,MethodName);

        System.Text.Encoding enc = System.Text.Encoding.UTF8;
        byte[] myByteArray = enc.GetBytes(XMLRequest);


         // Get the Unity WWWForm object (a post version)


        var form = new WWWForm();
        var url = WebServiceURL;

        //  Add a custom header to the request.
        //  Change the content type to xml and set the character set
        var headers = form.headers;
        headers["Content-Type"]="text/xml;charset=UTF-8";

        // Post a request to an URL with our rawXMLData and custom headers
        var www = new WWW(WebServiceURL,myByteArray,headers);

        //  Start a co-routine which will wait until our servers comes back

        StartCoroutine(WaitForRequest(www));
}

IEnumerator WaitForRequest(WWW www)
{
    yield return www;

    // check for errors
    if (www.error == null)
    {
        Debug.Log("WWW Ok!: " + www.text);
    } else {
        Debug.Log("WWW Error: "+ www.error);
    }    
}

>使用UTF8将XML编码为ByteArray
>创建一个新的Unity WWWForm
>创建一个HashTable,存储当前的http标头(如果有的话)并将内容类型覆盖为text / xml
>将该批次发送到服务器
>设置一个等待回复的协同程序

没有Unity发送

我发现在C#中开发一个库(我使用MonoDevelop的标准版本)比使用Unity更简单,所以如果wnat做同样的事情,那么C#中的equivelant发送逻辑就会低于它.

private     string normalXMLCall(int Staging,Hashtable Fields)
    {
        //  figure out who to call
        string  WebServiceURL   =   "http://LIVSERVER";
        if (Staging == 1) {
            WebServiceURL       =   "http://TESTSERVER";
        }

        WebServiceURL           +=  WebServer;

        //  Build the request

        XmlRpcParser    parser  =   new XmlRpcParser();
        string XMLRequest       = parser.buildXMLRPCRequest(Fields,MethodName);

        //  Fire it off

        HttpWebRequest httpRequest =(HttpWebRequest)WebRequest.Create(WebServiceURL);

        httpRequest.Method = "POST";

        //Defining the type of the posted data as XML
        httpRequest.ContentType = "text/xml";

        // string data = xmlDoc.InnerXml;
        byte[] bytedata = Encoding.UTF8.GetBytes(XMLRequest);

        // Get the request stream.
        Stream requestStream = httpRequest.GetRequestStream();

        // Write the data to the request stream.
        requestStream.Write(bytedata,bytedata.Length);
        requestStream.Close();

        //Get Response
        HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();

        // Get the stream associated with the response.
        Stream receiveStream = httpResponse.GetResponseStream ();

        // Pipes the stream to a higher level stream reader with the required encoding format. 
        StreamReader readStream = new StreamReader (receiveStream,Encoding.UTF8);

        string  ReceivedData    =   readStream.ReadToEnd ();
        httpResponse.Close ();
        readStream.Close ();

        return  ReceivedData;
    }
}

从XML中提取数据

我写了一个简单的解析器.应为下面的findNode函数的构造函数提供原始XML数据和要查找的子节点对象.如果该节点可以在XML字符串的最高级别找到,则它将返回该节点的值(作为字符串),如果找不到,则返回null.此解析器特定于“Simple XML-RPC”,需要一些工作来解码编码字符,但这应该很容易添加.

public string findNode(string Xml,string SearchForTag) {

    int     nestCounter     =   0;
    bool    FoundTag        =   false;
    int     FoundTagLevel   =   0;
    string  ReturnValue     =   null;

    //  Break it down by "<"
    string  []  TagArray    =   Xml.Split('<');

    for (int i=0;i<TagArray.Length;i++) {

        if (i>175 && i<180) {
            int Hello=1;
        }

        string  ThisLine    =   "<" + TagArray[i];
        if (ThisLine.Length <= 1)                                           continue;
        if ((ThisLine.Length >= 2) && (ThisLine.Substring(0,2) == "<?"))    continue;
        if ((ThisLine.Length >= 3) && (ThisLine.Substring(0,3) == "<--"))   continue;

        //  It can be a vector or a scalar - vectors are full of scalars so we'll

        ThisLine                =   ThisLine.Replace("  "," ");
        ThisLine                =   ThisLine.Replace("</","</");
        string  []  FieldArray  =   ThisLine.Split(' ');
        bool    AddLinetoResult =   FoundTag;

        //  nest counter is the level we are operating on. We only check the first
        //  Level. When a vector is found we increase the nestCount and we won't
        //  search for the ID

        if (nestCounter <= 1) { //  Initial array we are looking on level 1
            for (int a=0;a<FieldArray.Length;a++) {
                string  ThisTag =   FieldArray[a];
                string  []  TagValue    =   ThisTag.Split("=\"".tochararray(),5);

                //  Every TagValue is xx=yy pair... we want "ID=\"xxx\" 

                if (TagValue.Length >= 3) {
                    string  TagName =   TagValue[2];
                    if (TagName == SearchForTag) {
                        FoundTag        =   true;
                        FoundTagLevel   =   nestCounter;
                        //  This Could be a vector or Scalar so find the ">" in this string
                        //  and start adding from there
                        int TerminatePos    =   ThisLine.IndexOf(">");
                        if ((TerminatePos >= 0) && (TerminatePos < ThisLine.Length))  {
                            ReturnValue =   ThisLine.Substring(TerminatePos+1);
                        }
                        break;
                    }
                }
            }
        }

        if (FieldArray.Length > 0) {
            string  ThisField   =   FieldArray[0].ToLower();

            /*
             * If we are in the loop where we have found the tag,* we haven't changed level and this is the end of a scalar it must
             * mean that the tag was a scalar so we can safely leave Now.
             */
            if ((FoundTag) && (FoundTagLevel == nestCounter) && (ThisField == "</scalar>")) {
                break;
                // return ReturnValue;
            }
            //  If we end or leave a vector we change the nestCounter
            if (ThisField.IndexOf("<vector") >= 0) {
                nestCounter++;
            }
            else if (ThisField.IndexOf("</vector>") >= 0) {
                nestCounter--;
            }
        }

        //  If we have found our tag and the nest counte goes below the level 
        //  we where looking at - it's time to leave

        if (FoundTag) {
            if (nestCounter <= FoundTagLevel) {
                break;
                //return    ReturnValue;
            }
        }

        if (AddLinetoResult) {
            ReturnValue +=  ThisLine;
        }

    }

    //  You may wanna do some url decoding here....

    return ReturnValue;
}

相关文章

前言 本文记录unity3D开发环境的搭建 unity安装 unity有中文...
前言 有时候我们希望公告牌跟随镜头旋转永远平行面向屏幕,同...
前言 经过一段时间的学习与实际开发,unity3D也勉强算是强行...
前言 在unity中我们常用的获取鼠标点击的方法有: 1、在3D场...
前言 在之前的例子中,我们都没有用到unity的精髓,例如地形...
这篇文章将为大家详细讲解有关Unity3D中如何通过Animator动画...