简体   繁体   English

Unity3D XML(-RPC)和C#

[英]Unity3D XML(-RPC) and C#

I'm actually answering my own question here. 我其实在这里回答我自己的问题。

I must be the only person in the world who tried to do this but given that it has taken me about a week to work this out - I figured that if there is ever another person who wants to use XML(-RPC) in Unity - I'll save them a weeks hassle. 我必须是世界上唯一一个试图这样做的人,但考虑到我花了大约一个星期的时间来解决这个问题 - 我想如果有另一个人想在Unity中使用XML(-RPC) - 我会拯救他们几个星期的麻烦。

What I wanted to do is talk to one of our Game servers for things like leaderboards. 我想要做的是与我们的一个游戏服务器谈谈排行榜等事情。 This server "talks" XML-RPC and I soon figured out that that's not easy in Unity. 这台服务器“会谈”XML-RPC,我很快就发现在Unity中这并不容易。

Build XML to send to our servers 构建XML以发送到我们的服务器

I couldn't find a standard function in Unity to do this without adding very large amounts of overhead. 我没有在Unity中找到标准函数来执行此操作而不会增加非常大量的开销。 So I build the following procedure instead. 所以我建立了以下程序。

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;
}

The buildXMLRPCRequest is used to build XML. buildXMLRPCRequest用于构建XML。 You hand it a HashTable with fields you want to encode which may include objects of the types: int, string or Hashtable. 您可以将HashTable与要编码的字段一起交给HashTable,其中可能包含以下类型的对象:int,string或Hashtable。 It will return a beautifully formated (Simple) XML-RPC string which is ready to go to our server. 它将返回一个精美格式化的(简单)XML-RPC字符串,该字符串已准备好发送到我们的服务器。

Send 发送

To send XML to our servers, you need to issue a POST request with the mime type set to text/xml. 要将XML发送到我们的服务器,您需要发出一个POST请求,其mime类型设置为text / xml。 None of the standard C# methods can be used in Unity but using this with the output of the buildXMLRPCRequest logic works perfectly. Unity中不能使用任何标准C#方法,但使用buildXMLRPCRequest逻辑的输出可以很好地工作。 What it does: 它能做什么:

Sending in Unity 在Unity中发送

I used this code: 我用过这段代码:

    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);
    }    
}
  • encode the XML to a ByteArray using UTF8 使用UTF8将XML编码为ByteArray
  • Create a new Unity WWWForm 创建一个新的Unity WWWForm
  • Create a HashTable, store the current http headers (if any) and overwrite the content type to text/xml 创建一个HashTable,存储当前的http标头(如果有的话)并将内容类型覆盖为text / xml
  • Send that lot to the server 将该批次发送到服务器
  • Set up a Coroutine which waits for the reply 设置一个等待回复的协同程序

Sending without Unity 没有Unity发送

I found that developing a library in C# (I use the standards version of MonoDevelop) is much simpler then using Unity for everything so the equivelant send logic in C# is below if wnat to do the same. 我发现在C#中开发一个库(我使用MonoDevelop的标准版本)比使用Unity更简单,因此如果wnat做同样的事情,那么C#中的equivelant发送逻辑就会低于它。

    private     string NormalXMLCall(int Staging,
                                         string WebServer,
                                         string MethodName,
                                         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, 0, 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;
    }
}

Extract data from XML 从XML中提取数据

I wrote a simple parser. 我写了一个简单的解析器。 The constructor for the below findNode function should be given the raw XML data and the child node object you want to find. 应为下面的findNode函数的构造函数提供原始XML数据和要查找的子节点对象。 It will return the value of that node (as a string) if that node can be found on the highest level of the XML string or null if it can't find it. 如果该节点可以在XML字符串的最高级别找到,则它将返回该节点的值(作为字符串),如果找不到,则返回null。 This parser is specific to "Simple XML-RPC" and needs a bit of work to decode encoded characters but that should be simple to add. 此解析器特定于“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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM